C# 生产者/消费者
我最近遇到了生产者/消费者模式的 C# 实现。它非常简单而且(至少对我来说)非常优雅。
它似乎是在 2006 年左右设计的,所以我想知道这个实现是否
- 安全
- 仍然适用
代码如下(原始代码引用于 http://bytes .com/topic/net/answers/575276-生产者-消费者#post2251375)
using System;
using System.Collections;
using System.Threading;
public class Test
{
static ProducerConsumer queue;
static void Main()
{
queue = new ProducerConsumer();
new Thread(new ThreadStart(ConsumerJob)).Start();
Random rng = new Random(0);
for (int i=0; i < 10; i++)
{
Console.WriteLine ("Producing {0}", i);
queue.Produce(i);
Thread.Sleep(rng.Next(1000));
}
}
static void ConsumerJob()
{
// Make sure we get a different random seed from the
// first thread
Random rng = new Random(1);
// We happen to know we've only got 10
// items to receive
for (int i=0; i < 10; i++)
{
object o = queue.Consume();
Console.WriteLine ("\t\t\t\tConsuming {0}", o);
Thread.Sleep(rng.Next(1000));
}
}
}
public class ProducerConsumer
{
readonly object listLock = new object();
Queue queue = new Queue();
public void Produce(object o)
{
lock (listLock)
{
queue.Enqueue(o);
// We always need to pulse, even if the queue wasn't
// empty before. Otherwise, if we add several items
// in quick succession, we may only pulse once, waking
// a single thread up, even if there are multiple threads
// waiting for items.
Monitor.Pulse(listLock);
}
}
public object Consume()
{
lock (listLock)
{
// If the queue is empty, wait for an item to be added
// Note that this is a while loop, as we may be pulsed
// but not wake up before another thread has come in and
// consumed the newly added object. In that case, we'll
// have to wait for another pulse.
while (queue.Count==0)
{
// This releases listLock, only reacquiring it
// after being woken up by a call to Pulse
Monitor.Wait(listLock);
}
return queue.Dequeue();
}
}
}
i've recently come across a producer/consumer pattern c# implementation. it's very simple and (for me at least) very elegant.
it seems to have been devised around 2006, so i was wondering if this implementation is
- safe
- still applicable
Code is below (original code was referenced at http://bytes.com/topic/net/answers/575276-producer-consumer#post2251375)
using System;
using System.Collections;
using System.Threading;
public class Test
{
static ProducerConsumer queue;
static void Main()
{
queue = new ProducerConsumer();
new Thread(new ThreadStart(ConsumerJob)).Start();
Random rng = new Random(0);
for (int i=0; i < 10; i++)
{
Console.WriteLine ("Producing {0}", i);
queue.Produce(i);
Thread.Sleep(rng.Next(1000));
}
}
static void ConsumerJob()
{
// Make sure we get a different random seed from the
// first thread
Random rng = new Random(1);
// We happen to know we've only got 10
// items to receive
for (int i=0; i < 10; i++)
{
object o = queue.Consume();
Console.WriteLine ("\t\t\t\tConsuming {0}", o);
Thread.Sleep(rng.Next(1000));
}
}
}
public class ProducerConsumer
{
readonly object listLock = new object();
Queue queue = new Queue();
public void Produce(object o)
{
lock (listLock)
{
queue.Enqueue(o);
// We always need to pulse, even if the queue wasn't
// empty before. Otherwise, if we add several items
// in quick succession, we may only pulse once, waking
// a single thread up, even if there are multiple threads
// waiting for items.
Monitor.Pulse(listLock);
}
}
public object Consume()
{
lock (listLock)
{
// If the queue is empty, wait for an item to be added
// Note that this is a while loop, as we may be pulsed
// but not wake up before another thread has come in and
// consumed the newly added object. In that case, we'll
// have to wait for another pulse.
while (queue.Count==0)
{
// This releases listLock, only reacquiring it
// after being woken up by a call to Pulse
Monitor.Wait(listLock);
}
return queue.Dequeue();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
该代码比这更旧 - 我在 .NET 2.0 发布之前编写了它。生产者/消费者队列的概念比这方式要古老:)
是的,据我所知,该代码是安全的 - 但它有一些缺陷:
老实说,代码背后的想法比代码本身更重要。
The code is older than that - I wrote it some time before .NET 2.0 came out. The concept of a producer/consumer queue is way older than that though :)
Yes, that code is safe as far as I'm aware - but it has some deficiencies:
The ideas behind the code are more important than the code itself, to be honest.
您可以执行类似以下代码片段的操作。它是通用的,并且有一个方法将空值(或您想要使用的任何标志)排队以告诉工作线程退出。
代码取自此处:http://www.albahari.com/threading/part4。 aspx#_Wait_and_Pulse
You could do something like the following code snippet. It's generic and has a method for enqueue-ing nulls (or whatever flag you'd like to use) to tell the worker threads to exit.
The code is taken from here: http://www.albahari.com/threading/part4.aspx#_Wait_and_Pulse
那天,我从上面的代码和 文章系列它来自。所以正如 Jon 所说,它有很大的价值,而且确实安全、适用。
但是,从 .NET 4 开始,框架中有一个生产者-消费者队列实现。我自己才刚刚找到它,但到目前为止它已经满足了我需要的一切。
Back in the day I learned how Monitor.Wait/Pulse works (and a lot about threads in general) from the above piece of code and the article series it is from. So as Jon says, it has a lot of value to it and is indeed safe and applicable.
However, as of .NET 4, there is a producer-consumer queue implementation in the framework. I only just found it myself but up to this point it does everything I need.
如今,使用命名空间 System.Threading.Tasks.Dataflow 可以使用更现代的选项。它是异步/等待友好的并且更加通用。
更多信息请参见此处 如何:实现生产者-消费者数据流模式
它从 .Net Core 开始包含在内,对于较旧的 .Net,您可能需要安装与命名空间同名的包。
我知道这个问题很旧,但这是 Google 中第一次匹配我的请求,所以我决定更新这个主题。
These days a more modern option is available using the namespace System.Threading.Tasks.Dataflow. It's async/await friendly and much more versatile.
More info here How to: Implement a producer-consumer dataflow pattern
It's included starting from .Net Core, for older .Nets you may need to install a package with the same name as the namespace.
I know the question is old, but it's the first match in Google for my request, so I decided to update the topic.
在 C# 中实现生产者/消费者模式的一种现代且简单的方法是使用
System.Threading.Channels
。它是异步的,并使用 ValueTask 来减少内存分配。这是一个例子:A modern and simple way to implement the producer/consumer pattern in C# is to use
System.Threading.Channels
. It's asynchronous and usesValueTask
's to decrease memory allocations. Here is an example:警告:如果您阅读了评论,您就会明白我的答案是错误的:)
您的代码中可能存在死锁。
想象一下以下情况,为了清楚起见,我使用了单线程方法,但应该很容易通过睡眠转换为多线程:
如果这没有任何意义,请告诉我。
如果这一点得到证实,那么您问题的答案就是“不,这不安全”;)
我希望这有帮助。
Warning: If you read the comments, you'll understand my answer is wrong :)
There's a possible deadlock in your code.
Imagine the following case, for clarity, I used a single-thread approach but should be easy to convert to multi-thread with sleep:
Please do let me know if this doesn't make any sense.
If this is confirmed, then the answer to your question is, "no, it isn't safe" ;)
I hope this helps.