Java ExecutorService.newSingleThreadExecutor()的C#等价物,或者:如何序列化对资源的多线程访问

Dav*_*les 18 .net c# concurrency multithreading

我的代码中有几种情况,各种线程可以创建工作项,由于各种原因,不应该并行完成.我想确保工作以FIFO方式完成,无论它来自哪个线程.在Java中,我将工作项放在单线程上ExecutorService; 在C#中是否有等价物?我用Queue一堆和一些lock(){}块拼凑了一些东西,但是能够使用现成的和经过测试的东西会很好.

更新:有没有人有System.Threading.Tasks的经验?它有这种解决方案吗?我正在写一个MonoTouch的应用程序,所以谁知道,如果我甚至可以找到它的反向移植版本,我能得到工作,但它会至少是值得思考的未来.

更新#2对于C#开发人员不熟悉我说的Java库,基本上我想要的东西,可以让不同的线程交班工作项目等,所有这些工作项目将在单个线程中运行(这不是任何的调用线程).


更新,6/201:如果我现在正在构建一个类似的系统,我可能会根据Matt Craig的回答使用Reactive Extensions.我要离开扎卡里·耶茨的回答公认的一个,不过,因为如果你在思考的Rx你可能甚至不会问这个问题,我认为是比较容易bodge到预先的Rx程序.ConcurrentQueue

Zac*_*tes 4

更新:要解决有关浪费资源的评论(如果您不使用 Rx),您可以使用 a BlockingCollection(如果您使用默认构造函数,它会包装 a ConcurrentQueue)并调用.GetConsumingEnumerable()CancellationToken如果工作长时间运行,就会出现过载。请参阅下面的示例。


您可以使用ConcurrentQueue,(如果 monotouch 支持 .net 4?)它是线程安全的,我认为该实现实际上是无锁的。如果您有一个长时间运行的任务(例如在 Windows 服务中),这非常有效。

一般来说,您的问题听起来像是有多个生产者和一个消费者。

var work = new BlockingCollection<Item>();
var producer1 = Task.Factory.StartNew(() => {
    work.TryAdd(item); // or whatever your threads are doing
});
var producer2 = Task.Factory.StartNew(() => {
    work.TryAdd(item); // etc
});
var consumer = Task.Factory.StartNew(() => {
    foreach (var item in work.GetConsumingEnumerable()) {
        // do the work
    }
});
Task.WaitAll(producer1, producer2, consumer);
Run Code Online (Sandbox Code Playgroud)

如果您的工作项池有限,则 应该使用BlockingCollection 。这是一个 MSDN 页面,显示了所有新的并发集合类型。

  • 虽然此解决方案可能具有与 Java 解决方案相同的结果,但“消费者”线程将继续运行。TryDequeue 仅提供并发安全性,但如果队列中没有元素,它不会继续等待。如果队列中没有元素,则 while 循环中有一个线程正在运行。Java版本绝对不会这样浪费资源。“等待”-“通知”解决方案比这要好得多。 (3认同)