c#线程间通信

时间:2010-06-29 14:33:48

标签: c# .net multithreading

你好我想要线程来合作制作人和消费者。 消费者相当缓慢,生产者非常快,并且突然工作。

例如,消费者可以每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);
  }
}

4 个答案:

答案 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);
  }

}

这会使生产者减慢几毫秒,同时等待消费者唤醒并释放互斥锁。如果你能忍受那个。