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

时间:2021-04-30 07:09:51

I have a couple of situations in my code where various threads can create work items that, for various reasons, shouldn't be done in parallel. I'd like to make sure the work gets done in a FIFO manner, regardless of what thread it comes in from. In Java, I'd put the work items on a single-threaded ExecutorService; is there an equivalent in C#? I've cobbled something together with a Queue and a bunch of lock(){} blocks, but it'd be nice to be able to use something off-the-shelf and tested.

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

Update: Does anybody have experience with System.Threading.Tasks? Does it have a solution for this sort of thing? I'm writing a Monotouch app so who knows if I could even find a backported version of it that I could get to work, but it'd at least be something to think about for the future.

更新:有没有人有System.Threading.Tasks的经验?它有这种解决方案吗?我正在写一个Monotouch应用程序,所以谁知道我是否能找到它可以开始工作的它的后向版本,但它至少是为未来考虑的事情。

Update #2 For C# developers unfamiliar with the Java libraries I'm talking about, basically I want something that lets various threads hand off work items such that all those work items will be run on a single thread (which isn't any of the calling threads).

更新#2对于不熟悉我正在谈论的Java库的C#开发人员,基本上我想要一些东西让各个线程交出工作项,这样所有这些工作项都将在一个线程上运行(这不是任何一个调用线程)。


Update, 6/2018: If I was architecting a similar system now, I'd probably use Reactive Extensions as per Matt Craig's answer. I'm leaving Zachary Yates' answer the accepted one, though, because if you're thinking in Rx you probably wouldn't even ask this question, and I think ConcurrentQueue is easier to bodge into a pre-Rx program.

更新,6/201:如果我现在正在构建一个类似的系统,我可能会根据Matt Craig的回答使用Reactive Extensions。我离开Zachary Yates回答了被接受的答案,因为如果你在Rx中思考,你可能甚至都不会问这个问题,我认为ConcurrentQueue更容易进入前Rx程序。

5 个解决方案

#1


3  

You can use ConcurrentQueue, (if monotouch supports .net 4?) it's thread safe and I think the implementation is actually lockless. This works pretty well if you have a long-running task (like in a windows service).

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

Generally, your problem sounds like you have multiple producers with a single consumer.

通常,您的问题听起来就像您有多个生产者只有一个消费者。

var work = new ConcurrentQueue<Item>();
var producer1 = Task.Factory.StartNew(() => {
    work.Enqueue(item); // or whatever your threads are doing
});
var producer2 = Task.Factory.StartNew(() => {
    work.Enqueue(item); // etc
});
var consumer = Task.Factory.StartNew(() => {
    while(running) {
        Item item = null;
        work.TryDequeue(out item);
    }
});
Task.WaitAll(producer1, producer2, consumer);

You should use BlockingCollection if you have a finite pool of work items. Here's an MSDN page showing all of the new concurrent collection types.

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

#2


1  

I believe this can be done using a SynchronizationContext. However, I have only done this to post back to the UI thread, which already has a synchronization context (if told to be installed) provided by .NET -- I don't know how to prepare it for use from a "vanilla thread" though.

我相信这可以使用SynchronizationContext来完成。但是,我只是这样做回发到UI线程,它已经有.NET提供的同步上下文(如果被告知要安装) - 我不知道如何准备它从“vanilla线程”使用“不过。

Some links I found for "custom synchronizationcontext provider" (I have not had time to review these, do not fully understand the working/context, nor do I have any additional information):

我找到“自定义同步文本提供程序”的一些链接(我没有时间查看这些,不完全理解工作/上下文,也没有任何其他信息):

  1. Looking for an example of a custom SynchronizationContext (Required for unit testing)

    寻找自定义SynchronizationContext的示例(单元测试需要)

  2. http://codeidol.com/csharp/wcf/Concurrency-Management/Custom-Service-Synchronization-Context/

    http://codeidol.com/csharp/wcf/Concurrency-Management/Custom-Service-Synchronization-Context/

Happy coding.

快乐的编码。

#3


1  

There is a more contemporary solution now available - the EventLoopScheduler class.

现在有一个更现代的解决方案 - EventLoopScheduler类。

#4


0  

Not native AFAIK, but look at this: Serial Task Executor; is this thread safe?

不是原生的AFAIK,但看看这个:Serial Task Executor;这个线程安全吗?

#5


-2  

As I wrote in comments, you discovered by yourself that the lock statement can do the work.

正如我在评论中写的那样,你自己发现lock语句可以完成工作。

If you are interested in getting a "container" that can make simpler the job of managing a queue of work items, look at the ThreadPool class.

如果您有兴趣获得一个“容器”,可以简化管理工作项队列的工作,请查看ThreadPool类。

I think that, in a well designed architecture, with these two elemnts (ThreadPool class and lock statement) you can easily and succesfully serialize access to resources.

我认为,在一个设计良好的架构中,通过这两个元素(ThreadPool类和lock语句),您可以轻松且成功地序列化对资源的访问。

#1


3  

You can use ConcurrentQueue, (if monotouch supports .net 4?) it's thread safe and I think the implementation is actually lockless. This works pretty well if you have a long-running task (like in a windows service).

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

Generally, your problem sounds like you have multiple producers with a single consumer.

通常,您的问题听起来就像您有多个生产者只有一个消费者。

var work = new ConcurrentQueue<Item>();
var producer1 = Task.Factory.StartNew(() => {
    work.Enqueue(item); // or whatever your threads are doing
});
var producer2 = Task.Factory.StartNew(() => {
    work.Enqueue(item); // etc
});
var consumer = Task.Factory.StartNew(() => {
    while(running) {
        Item item = null;
        work.TryDequeue(out item);
    }
});
Task.WaitAll(producer1, producer2, consumer);

You should use BlockingCollection if you have a finite pool of work items. Here's an MSDN page showing all of the new concurrent collection types.

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

#2


1  

I believe this can be done using a SynchronizationContext. However, I have only done this to post back to the UI thread, which already has a synchronization context (if told to be installed) provided by .NET -- I don't know how to prepare it for use from a "vanilla thread" though.

我相信这可以使用SynchronizationContext来完成。但是,我只是这样做回发到UI线程,它已经有.NET提供的同步上下文(如果被告知要安装) - 我不知道如何准备它从“vanilla线程”使用“不过。

Some links I found for "custom synchronizationcontext provider" (I have not had time to review these, do not fully understand the working/context, nor do I have any additional information):

我找到“自定义同步文本提供程序”的一些链接(我没有时间查看这些,不完全理解工作/上下文,也没有任何其他信息):

  1. Looking for an example of a custom SynchronizationContext (Required for unit testing)

    寻找自定义SynchronizationContext的示例(单元测试需要)

  2. http://codeidol.com/csharp/wcf/Concurrency-Management/Custom-Service-Synchronization-Context/

    http://codeidol.com/csharp/wcf/Concurrency-Management/Custom-Service-Synchronization-Context/

Happy coding.

快乐的编码。

#3


1  

There is a more contemporary solution now available - the EventLoopScheduler class.

现在有一个更现代的解决方案 - EventLoopScheduler类。

#4


0  

Not native AFAIK, but look at this: Serial Task Executor; is this thread safe?

不是原生的AFAIK,但看看这个:Serial Task Executor;这个线程安全吗?

#5


-2  

As I wrote in comments, you discovered by yourself that the lock statement can do the work.

正如我在评论中写的那样,你自己发现lock语句可以完成工作。

If you are interested in getting a "container" that can make simpler the job of managing a queue of work items, look at the ThreadPool class.

如果您有兴趣获得一个“容器”,可以简化管理工作项队列的工作,请查看ThreadPool类。

I think that, in a well designed architecture, with these two elemnts (ThreadPool class and lock statement) you can easily and succesfully serialize access to resources.

我认为,在一个设计良好的架构中,通过这两个元素(ThreadPool类和lock语句),您可以轻松且成功地序列化对资源的访问。