【问题标题】:How to keep object member variables private to a thread in a multi threaded environment如何在多线程环境中保持对象成员变量对线程私有
【发布时间】:2011-12-01 09:53:30
【问题描述】:

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

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

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

我感觉QueryThreadProcess 的私​​有成员在所有线程之间共享。我怎样才能使它们对每个线程私有,但也可以将每个线程与外部类分开杀死?

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

这是我的经理班:

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;
    }
}

这是我的 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;
        }
    }
}

【问题讨论】:

  • 您正在为每个查询创建一个 QueryThreadProcess 实例,因此不会发生私有成员共享,因为每个实例都是完全独立的。
  • QueryThreadProcess 的私有成员确实是每个实例私有的。也就是说,QueryManager 中的 _queries 字典不是线程安全的,所以我建议锁定对字典的任何访问。此外,如果您希望同时调用其中一些方法,则可能存在其他竞争条件的微妙之处。

标签: c# asp.net .net multithreading


【解决方案1】:

它并没有真正回答您的问题,但使用 .NET 4 的 Task Parallel Library 看起来像一种更现代的方法可以为您省去一些麻烦。不需要自己控制线程。看起来您可以将您的类重构为几行代码并摆脱所描述的问题。

【讨论】:

    【解决方案2】:

    .NET 4 有 ThreadLocal&lt;T&gt; 你可能会感兴趣

    【讨论】:

      【解决方案3】:

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

      _isStoppingQuery 和 _isKilled 由工作线程和控制线程访问。因此,应该将它们声明为 volatile,以确保它们不会在处理器寄存器中兑现并且不会受到执行重新排序的影响。

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

      【讨论】:

      • 附带说明,由 volatile 强制执行的保证比由 MemoryBarrier 强制执行的保证要弱(包括使用锁时隐式创建的保证。)在这里很容易弄错细微之处,所以我会除非您是专门设计无锁数据结构和算法的线程专家,否则建议不要使用 volatile(或者甚至是显式的 MemoryBarrier)。
      猜你喜欢
      • 2012-09-13
      • 1970-01-01
      • 1970-01-01
      • 2013-01-31
      • 2018-12-08
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多