如何在多线程环境中保持对象成员变量为线程私有

时间:2023-01-15 15:17:37

I have a multi threaded program that opens a few threads to query an external CRM and save the results in an in-memory IDictionary in order to speed up the system.

我有一个多线程程序,它打开几个线程来查询外部的CRM,并将结果保存在内存中的IDictionary中,以加快系统的速度。

I'm a little confused about multi threading and critical sections. I want my class QueryThreadProcess to have a thread which runs the query and to manage starting and stopping the query. It has an object of type query and saves the results in a list.

我对多线程和关键部分有点困惑。我希望我的类QueryThreadProcess有一个运行查询的线程,并管理启动和停止查询。它有一个查询类型的对象,并将结果保存在一个列表中。

The class QueryManager will kill all the processes or start all processes, basically collection wide methods.

类QueryManager将杀死所有进程或启动所有进程,基本上是集合范围内的方法。

I have a feeling that the private members for QueryThreadProcess are shared between all threads. How would I be able to make them private to each thread, but also kill each thread separately from an external class?

我感觉QueryThreadProcess的私有成员在所有线程之间共享。我怎样才能使它们对每个线程都是私有的,同时又能将每个线程与外部类分开?

I don't want to lock because I want all the threads to run parallel.

我不想锁定,因为我想让所有的线程并行运行。

Here is my manager class:

这是我的经理课程:

public class QueryManager
{
    private IDictionary<int, QueryThreadProcess> _queries;

    public QueryManager()
    {
        _queries = new Dictionary<int, QueryThreadProcess>();
    }

    public void Start()
    {
        CreateQueryThreadsFromDb();
        StartAllThreads();
    }

    private void StartAllThreads()
    {
        if (_queries != null && _queries.Count > 0)
        {
            StopThreadsAndWaitForKill();

        }

        foreach (var query in _queries)
            query.Value.Start();
    }

    private void CreateQueryThreadsFromDb()
    {
        var queries = new QueryProvider().GetAllQueries();
        if (_queries != null && _queries.Count > 0)
        {
            StopThreadsAndWaitForKill();
            _queries.Clear();
        }

        foreach (var query in queries)
            _queries.Add(query.Id, new QueryThreadProcess(query));

    }

    private void StopThreadsAndWaitForKill()
    {
        KillAllThreads();
        while (!AreAllThreadsKilled()) { }
    }

    private void KillAllThreads()
    {
        foreach (var query in _queries)
            query.Value.Kill();
    }

    private bool AreAllThreadsKilled()
    {
        return _queries.All(query => query.Value.IsKilled);
    }

    public IList<User> GetQueryResultById(int id)
    {
        return _queries[id].Result;
    }
}

and here is my class for QueryProcesses which holds the threads that do the actual query:

这是我的QueryProcesses类,它包含执行实际查询的线程:

using System.Collections.Generic;
using System.Threading;
using Intra.BLL.MessageProviders;
using Intra.BO;
using Intra.BO.Messages;


namespace Intra.BLL.QueryProcess
{
    internal class QueryThreadProcess
    {
        private readonly Thread _thread;
        private readonly Query _query;
        private bool _isStoppingQuery = false;
        private bool _isKilled = true;

        private IList<User> _result;
        private readonly object _objSync = new object();

        public QueryThreadProcess(Query query)
        {
            _query = query;
            _thread = new Thread(RetrieveQueries);
        }

        public void Start()
        {
            _isStoppingQuery = true;
            while (!_isKilled) { }
            _isStoppingQuery = false;
            _thread.Start();
        }

        private void RetrieveQueries()
        {
             const string BROKERNAME = "bla";

            _isKilled = false;
            while (!_isStoppingQuery)
            {
                Broker broker = new BrokerProvider().GetBrokerByName(BROKERNAME);
                var users = new QueryProvider().GetUserObjectsByQuery(_query, ParaTokenGenerator.GetBrokerAuthToken(broker));
                _result = users;
            }
            _isKilled = true;
        }

        public bool IsKilled
        {
            get { return _isKilled; }
        }


        public IList<User> Result
        {
            get
            {
                lock (_objSync)
                    return _result;
            }
        }

        public void Kill()
        {
            _isStoppingQuery = true;
        }
    }
}

3 个解决方案

#1


3  

It doesn't really answer your question, but it looks like a more modern approach using the Task Parallel Library of .NET 4 could save you some headache. Controlling Threads by yourself isn't necessary. It looks like you could refactor your classes to a few lines of code and get rid of the described problems.

它并没有真正回答你的问题,但是使用。net 4的任务并行库看起来是一种更现代的方法,可以省去一些麻烦。自己控制线程是不必要的。看起来您可以将类重构为几行代码,并消除所描述的问题。

#2


0  

.NET 4 has ThreadLocal<T> which may be of interest to you

.NET 4有ThreadLocal ,您可能会感兴趣

#3


0  

The _thread and _query fields probably don’t matter as they are declared readonly and are not changed after each thread is run. These are not shared between worker threads as they are private to the class and you create a separate instance of the class for each thread.

_thread和_query字段可能并不重要,因为它们是被声明为只读的,并且在每个线程运行后不会更改。它们不会在工作线程之间共享,因为它们是类的私有属性,并且为每个线程创建一个单独的类实例。

_isStoppingQuery and _isKilled are accessed by both the worker thread and the controlling thread. As such these should be declared volatile to ensure they are not cashed in a processor register and don’t suffer from execution reordering.

工作线程和控制线程都可以访问_isStoppingQuery和_iskill。因此,应该声明这些变量为volatile类型,以确保它们不会在处理器寄存器中兑现,并且不会受到执行重排序的影响。

There is a potential issue with _result. The lock in Result/get is not enough to protect the contents of _result. It is only protecting the reference to the list not the list itself. However as your worker thread is only overwriting the reference each cycle it may not be an issue. I would probably do away with the lock on _objSync and declare _result volatile too.

_result有一个潜在的问题。Result/get中的锁不足以保护_result的内容。它只是保护对列表的引用,而不是列表本身。但是,由于您的工作线程只覆盖每个周期的引用,所以它可能不是问题。我可能会删除_objSync上的锁并声明_result volatile。

#1


3  

It doesn't really answer your question, but it looks like a more modern approach using the Task Parallel Library of .NET 4 could save you some headache. Controlling Threads by yourself isn't necessary. It looks like you could refactor your classes to a few lines of code and get rid of the described problems.

它并没有真正回答你的问题,但是使用。net 4的任务并行库看起来是一种更现代的方法,可以省去一些麻烦。自己控制线程是不必要的。看起来您可以将类重构为几行代码,并消除所描述的问题。

#2


0  

.NET 4 has ThreadLocal<T> which may be of interest to you

.NET 4有ThreadLocal ,您可能会感兴趣

#3


0  

The _thread and _query fields probably don’t matter as they are declared readonly and are not changed after each thread is run. These are not shared between worker threads as they are private to the class and you create a separate instance of the class for each thread.

_thread和_query字段可能并不重要,因为它们是被声明为只读的,并且在每个线程运行后不会更改。它们不会在工作线程之间共享,因为它们是类的私有属性,并且为每个线程创建一个单独的类实例。

_isStoppingQuery and _isKilled are accessed by both the worker thread and the controlling thread. As such these should be declared volatile to ensure they are not cashed in a processor register and don’t suffer from execution reordering.

工作线程和控制线程都可以访问_isStoppingQuery和_iskill。因此,应该声明这些变量为volatile类型,以确保它们不会在处理器寄存器中兑现,并且不会受到执行重排序的影响。

There is a potential issue with _result. The lock in Result/get is not enough to protect the contents of _result. It is only protecting the reference to the list not the list itself. However as your worker thread is only overwriting the reference each cycle it may not be an issue. I would probably do away with the lock on _objSync and declare _result volatile too.

_result有一个潜在的问题。Result/get中的锁不足以保护_result的内容。它只是保护对列表的引用,而不是列表本身。但是,由于您的工作线程只覆盖每个周期的引用,所以它可能不是问题。我可能会删除_objSync上的锁并声明_result volatile。