【发布时间】:2012-12-18 03:43:19
【问题描述】:
我有一个处理作业的 Windows 服务。此服务中的作业是一系列操作,例如:
A -> B -> C -> D -> E
每个工作都应该完全独立于其他工作。当前实现在其自己的线程上处理每个作业(在本例中为new System.Threading.Thread),这在大多数情况下运行良好。然而,事实证明 B 动作不是线程安全的,两个线程不应该同时处理 B 动作。如果两个线程同时尝试执行 B 动作,有时会出现奇怪的结果或错误。请注意,其他操作可能由多个作业同时执行而不会产生任何不良影响。
我不想尝试以线程安全的方式重新实现 B 操作(我预计这将需要大量资源来实现和测试),我只想将每个 B 操作限制为单个线程一次。
现在对我来说显而易见的初始解决方案是在 B 操作周围放置一个 lock,这肯定会将其限制为单个线程。然而,这个解决方案似乎不能保证所有等待的线程都有机会处理 B 动作。这是因为线程not guaranteed 以 FIFO 或一致的方式排队。我觉得这在理论上可能会导致线程饥饿。因此,我不太愿意实施此解决方案。
所以我的问题是我怎样才能在 .NET 中实现更好的(可能是 FIFO)作业队列,以保证所有作业都将通过 B 操作,一个接一个?
我目前的想法是我可以维护某种“管理器”线程,其工作是从队列中拉出一个作业线程,并在 B 操作明确时执行它。 (也许我正在描述实现我自己的调度程序?)但这似乎很粗糙,也许 .NET(我正在使用 .NET 4.0)在它的库中有更好的工具来解决这种情况。
【问题讨论】:
-
你看 BlockingCollection msdn.microsoft.com/en-us/library/dd267312.aspx
-
它不是严格意义上的 FIFO 的事实并不真的可能是一个真正的问题。实际上,所有的工作都会通过使用锁来完成。
-
@MarcGravell 我同意你的看法,主要是因为该服务可能会用完要运行的作业。但我仍然对此感到不安,特别是因为在 MSDN 文档中似乎没有提到(我可以找到)线程如何在锁定或监视器上排队。如果系统负载很重,我也想避免工作不得不等待不公平的长时间。我的担心是没有根据的吗?
-
@StephenBooher 这取决于排队率与处理率。如果处理速度快了足够的幅度,那么队列永远不会足够长,足以让它变得重要。如果吞吐量仅勉强够用,那么饥饿是一个更严重的问题。
-
如果你不并行,那么 BlockingCollection 将保持顺序。我在插入 SQL 的最后一个队列中以这种方式使用它,并且需要保留顺序以最大程度地减少索引碎片。您可以设置队列的最大大小。