【问题标题】:Most Efficient Lock for Many Writers Single Reader Concurrency Model?多写者单读者并发模型最有效的锁?
【发布时间】:2011-08-19 00:06:30
【问题描述】:

所以我有许多线程正在向我提供输入数据,这些数据必须由单个线程按到达顺序进行处理。目前,所有输入项最终都插入到队列中,并且对队列的读/写受到 C# lock 语句的保护。然而,随着时间的推移,应用程序的 CPU 使用率上升到不可接受的水平,并且分析器表示大部分 CPU 时间都花在了 lock 语句本身上。是否有一种更有效的同步方法可以代替锁,它支持多个写入器和一个读取器?

【问题讨论】:

    标签: c# .net multithreading concurrency synchronization


    【解决方案1】:

    听起来作家们正在互相争夺锁。考虑一个模型,其中每个写入器都有自己的队列,读取器使用Peek method 从每个队列中读取第一条消息而不删除它。然后,阅读器可以继续在队列之间迭代,从每个队列的第一个项目集中查看第一个项目,然后删除并处理第一个项目。它会比您当前的架构慢,但应该消除编写器之间的锁争用。

    一个简单的例子可能如下所示:

    public class TimestampedItem<T> : IComparable<TimestampedItem<T>>
    {
        public DateTime TimeStamp { get; set; }
        public T Data { get; set; }
        public int CompareTo(TimestampedItem<T> other)
        {
            return TimeStamp.CompareTo(other.TimeStamp);
        }
    }
    
    public void ReadFirstFromEachQueue<T>(IEnumerable<Queue<TimestampedItem<T>>> queues)
    {
        while (true)
        {
            var firstItems = new List<TimestampedItem<T>>(queues.Select(q => { lock (q) { return q.Peek(); } }));
                ProcessItem(firstItems.OrderBy(tsi => tsi.TimeStamp).First());
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      如果您使用.net 版本4.0,则可以使用ConcurrentQueue,它是ConcurrentCollections 的一部分,而不是普通的Queue,然后在读/写数据时解除锁定到队列,ConcurrentCollections 被设计用于处理并发读/写与无锁代码..

      如果您不使用4.0,则只能在没有其他锁后跟时锁定,您可以通过使用Monitor.TryEnter 而不是lock 来实现,注意lock 本身是Monitor.Enter 和@ 987654331@组合..,示例实现为:

      private readonly object _syncObject = new object();
      
      private bool TryUpdate(object someData)
      {
          if (Monitor.TryEnter(_syncObject))
          {
              try
              {
                  //Update the data here.
      
                  return true;
              }
              finally
              {
                  Monitor.Exit(_SyncObject);
              }
          }
      
          return false;
      }
      

      【讨论】:

      • Monitor.TryEnter 比 Monitor.Enter 有什么优势?
      • 第一个“Monitor.TryEnter”不会阻塞等待进入,如果lock正在使用,它将返回false而不是等待它在该行空闲..
      【解决方案3】:

      这可能会对您的应用程序产生重大影响,但您可以考虑将队列设置在应用程序外部(例如 MSMQ),然后您可以让编写器线程向该队列写入它们的核心内容。然后,您的读者可以在准备好物品时将其挑选出来。如果您的大部分 cpu 时间只是锁定在您的队列周围(我假设您实际上并没有锁定正在放入队列中的项目的工作),那么将队列放在您的应用程序外部可能真的有帮助。理想情况下,您还可以将写入和读取拆分为单独的进程。

      要检查的另一件事是,您锁定的对象没有被用于锁定应用程序中的其他位置。监视器(lock 语句背后的东西)可能是最轻量级的线程同步方法,因此最好重新设计事物以避免锁定在处理项目的同一进程中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-28
        • 1970-01-01
        • 2018-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-26
        • 1970-01-01
        相关资源
        最近更新 更多