【问题标题】:Implementing a lock-free queue (for a Logger component)实现无锁队列(用于 Logger 组件)
【发布时间】:2012-01-04 23:51:14
【问题描述】:

我正在设计一个新的改进的 Logger 组件(.NET 3.5,C#)。

我想使用无锁实现。

日志事件将从(可能)多个线程发送,尽管只有一个单个线程将实际输出到文件/其他存储介质。

本质上,所有的作者都*将他们的数据排入队列*,以便由其他进程(LogFileWriter)检索。

这可以通过无锁的方式实现吗?我在网上找不到这个特定问题的直接参考。

【问题讨论】:

  • ..可能是因为没有需求。大多数日志记录活动的一端是间歇性的日志供应,另一端是慢速磁盘。锁定的、由内核管理的队列对于此类功能最有效。是否有一些特殊原因说明您的应用可能需要无锁队列?
  • 我不确定大多数记录器是否使用锁定,即使可以不使用它也是如此。我的要求是高性能,这意味着尽快释放调用记录器的线程。在这种情况下,抢锁似乎是一种浪费。
  • 我同意汉斯的观点。 logging.apache.org/log4net 很有可能比自制系统更快,并且肯定会花费更少的时间进行开发和调试以正常运行。
  • 关于性能记录器,使用 DateTime.UtcNow。它比 DateTime.Now 快得多,并且在分析我的应用程序时对日志记录线程产生了重大影响。我使用 log4net。
  • @liortal - 允许多个生产者的无锁队列实现非常简单。然后是如何向消费者/记录器线程发出一个新条目已添加的信号的问题,以便它可以检索它并记录它。我会使用信号量,但还有其他 condvar 事物。无论您使用什么,这可能意味着内核调用向记录器发出信号。以无锁方式避免内核调用的唯一途径是生产者对队列进行自旋锁并确定队列计数不为零。如果你能做到这些,你就不需要任何帮助!

标签: c# .net multithreading concurrency lockless


【解决方案1】:

如果你发现在这种情况下使用锁太慢了,你的问题就更大了。在我的系统(2.0 GHz Core 2 Quad)上,如果没有争用锁,大约需要 75 纳秒。当然,当它发生争执时,它会花费更长的时间。但由于锁只是保护对EnqueueDequeue 的调用,日志写入的总时间不太可能超过75 纳秒。

如果锁是个问题——也就是说,如果你发现你的线程在那个锁后面排队并导致你的应用程序明显变慢——那么创建无锁队列不太可能会有很大帮助。为什么?因为如果你真的在日志中写了那么多,你的无锁阻塞队列会很快填满,你会被 I/O 子系统的速度限制。

我有一个多线程应用程序,它每秒将大约 200 个日志条目写入受简单锁保护的Queue<string>。我从未注意到任何重大的锁争用,并且处理速度丝毫没有减慢。 75 ns 与执行其他所有操作所需的时间相比相形见绌。

【讨论】:

    【解决方案2】:

    这种无锁队列的实现可能会有所帮助,其中队列是您用于将要出列并由记录器写出的项目排入队列的数据结构。

    http://www.boyet.com/Articles/LockfreeQueue.html

    您还可以查看 .Net 4 的 ConcurrentQueue

    http://www.albahari.com/threading/part5.aspx#_Concurrent_Collections

    http://geekswithblogs.net/BlackRabbitCoder/archive/2011/02/10/c.net-little-wonders-the-concurrent-collections-1-of-3.aspx

    【讨论】:

    • 谢谢,我会检查一下。你知道这是否适用于所有不同的场景(例如:多读者、多作者、单读者、多作者等?)
    • 我知道它们,但没有机会使用它们,因此无法明确回答。在并发集合的情况下,由于它们与 .net 的并行添加一起出现,我认为多个读者/作者是意图。在您的情况下,您可能需要多个编写者、单个阅读器,这对队列来说应该不会太繁重。
    • 斧头正走在正确的轨道上。您有一个生产者/消费者模式,因此带有 ConcurrentQueue 的 BlockingCollection 可以解决问题。 msdn.microsoft.com/en-us/library/dd267312.aspx。是的,它适用于不同的场景。
    【解决方案3】:

    那里有很多不同的无锁队列实现。

    我在http://hackcraft.github.com/Ariadne/ 使用的方法很简单,并且是开源的,因此您可以在必要时对其进行调整。

    ConcurrerntQueue 也是无锁的,并且可能可以很好地用于大多数用途,尽管在 Ariadne 中有一些成员支持其他操作(例如将整个内容的枚举作为原子操作出队,这允许更快的枚举由单个消费者)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-30
      • 1970-01-01
      相关资源
      最近更新 更多