对于许多写入者来说最有效的锁定单一读取器并发模型?
因此,我有许多线程向我提供输入数据,这些数据必须由单个线程按到达顺序进行处理。目前,所有输入项最终都插入到队列中,并且对队列的读/写操作受到 C# 锁定语句的保护。然而,随着时间的推移,应用程序的 CPU 使用率上升到不可接受的水平,分析器表示大部分 CPU 时间都花在了锁定语句本身上。是否有一种更有效的同步方法可以代替锁,支持多个写入器和一个读取器?
So I have many threads that are feeding me input data, which must be processed by a single thread in order of arrival. Currently, all the input items wind up inserted in a queue, and read/writes to the queue are protected with the C# lock statement. However, over time, the CPU usage of the application rises to an unacceptable level, and the profiler says that the majority of the CPU time is being spent on the lock statement itself. Is there a more efficient synchronization method available in place of the lock, that supports many writers and one reader?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
听起来编剧们正在互相争夺锁。考虑一个模型,其中每个写入器都有自己的队列,并且读取器使用 Peek 方法 从每个队列读取第一条消息而不删除它。然后,读取器可以继续在队列之间进行迭代,从每个队列中查看第一项的集合中的第一项,然后删除并处理该第一项。它会比您当前的体系结构慢,但应该消除写入者之间的锁争用。
一个简单的例子可能如下所示:
It sounds like the writers are contending with each other for the locks. Consider a model where each writer has its own queue, and where the reader uses the Peek method to read the first message off of each queue without removing it. The reader can then keep iterating between the queues, peeking the first item among the set of first items from each queue, and then removing and processing that first item. It will be slower than your current architecture, but should eliminate the lock contention among the writers.
A trivial example might look like:
如果您使用的是 .net 版本
4.0
,则可以使用ConcurrentQueue
(它是ConcurrentCollections
的一部分),而不是普通的Queue
然后在将数据读/写到队列时摆脱锁定,ConcurrentCollections
设计用于使用无锁代码处理并发读/写。如果您是不使用
4.0
您可以仅在没有其他锁跟随的情况下才锁定,您可以通过使用Monitor.TryEnter
而不是lock
来实现这一点,请注意< code>lock 本身是Monitor.Enter
和Monitor.Exit
组合..,示例实现如下:If you are using .net version
4.0
you can then use theConcurrentQueue
which is part of theConcurrentCollections
, instead of the normalQueue
and then get rid of the lock when read/write your data to the queue, theConcurrentCollections
are designed to be used to handle concurrent read/write with lock free code..If you are not using
4.0
you can is to lock only if no other lock is heeled, you can achieve that by usingMonitor.TryEnter
instead oflock
note thatlock
itself isMonitor.Enter
andMonitor.Exit
combination.., sample implementation would be:这可能对您的应用程序来说是一个很大的变化,但您可以考虑将队列设置为应用程序的外部(例如 MSMQ),然后您可以让编写器线程根据其核心内容写入该队列。然后,您的读者可以在物品准备好后将其取下。如果您的大部分 cpu 时间只是在队列周围的锁上(我假设您实际上并没有锁定放在队列中的项目的工作),那么将队列外部放置到您的应用程序确实会有所帮助。理想情况下,您还可以将写入和读取分成单独的过程。
另一件需要检查的事情是您锁定的对象没有被用来锁定应用程序中的其他位置。监视器(锁定语句背后的东西)可能是最轻量级的线程同步方法,因此最好重新构建事物以避免在处理项目的同一进程中锁定。
It might be a big change to your app, but you could consider making your queue external to your application (for example MSMQ) and then you could have your writer threads writing to that queue to their hearts content. Your reader could then just pick the items off when its ready. If the bulk of your cpu time is just on the lock around your queue (I assume you are not actually locking around the work on the items being put on the queue), then putting the queue exteral to your app could really help. Ideally you could also split the writing and reading into seperate processes.
Another thing to check is that the object your are locking on is not being used to lock somewhere else in your app. A monitor (the thing behind the lock statement) is probably the lightest weight thread sync method there is, so might be best to re-architect things to avoid locking in the same process that is doing the processing of items.