你好我想要线程来合作制作人和消费者。 消费者相当缓慢,生产者非常快,并且突然工作。
例如,消费者可以每20秒处理一条消息,并且生产者可以在一秒钟内生成10条消息,但是在很长一段时间内就会生成一条消息,这样消费者就可以赶上来。我想要类似的东西:
Stream commonStream;
AutoResetEvent commonLock;
void Producer()
{
while (true)
{
magic.BlockUntilMagicAvalible();
byte[] buffer = magic.Produce();
commonStream.Write(buffer);
commonLock.Set();
}
}
void Consumer()
{
while(true)
{
commonLock.WaitOne();
MagicalObject o = binarySerializer.Deserialize(commonStream);
DoSomething(o);
}
}
答案 0 :(得分:11)
如果你有.Net 4.0或更高版本,你可以使用BlockingCollection
int maxBufferCap = 500;
BlockingCollection<MagicalObject> Collection
= new BlockingCollection<MagicalObject>(maxBufferCap);
void Producer()
{
while (magic.HasMoreMagic)
{
this.Collection.Add(magic.ProduceMagic());
}
this.Collection.CompleteAdding();
}
void Consumer()
{
foreach (MagicalObject magicalObject in this.Collection.GetConsumingEnumerable())
{
DoSomthing(magicalObject);
}
}
如果缓冲区中没有数据,foreach
行将会休眠,当集合中添加了某些内容时,它也会自动将其唤醒。
我设置最大缓冲区的原因是,如果你的生产者比消费者快得多,你可能最终消耗大量内存,因为越来越多的对象被放入集合中。通过在达到缓冲区大小时创建阻塞集合时设置最大缓冲区大小,生成器上的Add
调用将阻塞,直到消费者从项目中删除项目为止。
BlockingCollection
类的另一个好处是它可以拥有任意数量的生产者和消费者,它不需要是1:1的比例。如果DoSomthing
支持它,则每个计算机核心可以有一个foreach
循环(甚至可以使用Parallel.ForEach
并使用耗尽的枚举作为数据源)
void ConsumersInParalell()
{
//This assumes the method signature of DoSomthing is one of the following:
// Action<MagicalObject>
// Action<MagicalObject, ParallelLoopState>
// Action<MagicalObject, ParallelLoopState, long>
Paralell.ForEach(this.Collection.GetConsumingEnumerable(), DoSomthing);
}
答案 1 :(得分:1)
我会阅读以下文章来描述你的问题。基本上你没有为你的工作单元获得正确的隔离。
http://blogs.msdn.com/b/ricom/archive/2006/04/24/582643.aspx http://blogs.msdn.com/b/ricom/archive/2006/04/26/584802.aspx
答案 2 :(得分:0)
您可以使用队列和计时器获得所需内容。生产者将值添加到队列并启动使用者计时器。消费者计时器的已用事件(在Threadpool线程上)会停止计时器,并循环通过队列直到它为空,然后消失(没有不必要的轮询)。生产者可以在消费者仍在运行时添加到队列中。
System.Timers.Timer consumerTimer;
Queue<byte[]> queue = new Queue<byte[]>();
void Producer()
{
consumerTimer = new System.Timers.Timer(1000);
consumerTimer.Elapsed += new System.Timers.ElapsedEventHandler(consumerTimer_Elapsed);
while (true)
{
magic.BlockUntilMagicAvailable();
lock (queue)
{
queue.Enqueue(magic.Produce());
if (!consumerTimer.Enabled)
{
consumerTimer.Start();
}
}
}
}
void consumerTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
while (true)
{
consumerTimer.Stop();
lock (queue)
{
if (queue.Count > 0)
{
DoSomething(queue.Dequeue());
}
else
{
break;
}
}
}
}
答案 3 :(得分:-1)
我使用互斥锁。这个想法是两个都在不同的线程中运行。 Consumer线程由互斥锁启动锁定,它将无限期地停留,直到生产者发布。然后它将并行处理数据,让Producer继续。消费者将在完成后重新锁定。
(代码启动线程,为简洁省略了其他质量位。)
// Pre-create mutex owned by Producer thread, then start Consumer thread.
Mutex mutex = new Mutex(true);
Queue<T> queue = new Queue<T>();
void Producer_AddData(T data)
{
lock (queue) {
queue.Enqueue(GetData());
}
// Release mutex to start thread:
mutex.ReleaseMutex();
mutex.WaitOne();
}
void Consumer()
{
while(true)
{
// Wait indefinitely on mutex
mutex.WaitOne();
mutex.ReleaseMutex();
T data;
lock (queue) {
data = queue.Dequeue();
}
DoSomething(data);
}
}
这会使生产者减慢几毫秒,同时等待消费者唤醒并释放互斥锁。如果你能忍受那个。