在使用BlockingCollection作为队列

时间:2016-07-15 16:15:53

标签: c# .net multithreading asynchronous blockingcollection

我正在研究服务器端控制台应用程序,该应用程序从多个WCF服务接收数据,对其进行一些工作,然后使用SignalR通过单个连接将结果转发到IIS服务器。

我尝试使用生产者消费者模式实现这一点,其中WCF服务是生产者,使用SignalR发送数据的类是消费者。对于队列,我使用了BlockingCollection

但是,当使用await / async在消费者中发送数据时,循环会一直停滞,直到所有其他线程都已完成向队列添加数据。

出于测试目的,我已将实际发送数据的代码替换为Task.Delay(1000).Wait();await Task.Delay(1000);,这些代码也会被卡住。 一个简单的Thread.Sleep(1000);似乎工作正常,导致我认为异步代码是问题。

所以我的问题是:是否存在阻止异步代码在while循环中完成的内容?我错过了什么?

我正在开始这样的消费者线程:

new Thread(Worker).Start();

消费者代码:

private void Worker()
{
    while (!_queue.IsCompleted)
    {
        IMobileMessage msg = null;
        try
        {
            msg = _queue.Take();
        }
        catch (InvalidOperationException)
        {
        }

        if (msg != null)
        {
            try
            {
                Trace.TraceInformation("Sending: {0}", msg.Name);
                Thread.Sleep(1000); // <-- works
                //Task.Delay(1000).Wait(); // <-- doesn't work
                msg.SentTime = DateTime.UtcNow;
                Trace.TraceInformation("X sent at {1}: {0}", msg.Name, msg.SentTime);
            }
            catch (Exception e)
            {
                TraceException(e);
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

正如消费者正确指出的那样,BlockingCollection(顾名思义)仅用于阻止代码,并且在异步代码中效果不佳。

存在与异步兼容的生产者/消费者队列,例如BufferBlock<T>。在这种情况下,我认为ActionBlock<T>会更好:

private ActionBlock<IMobileMsg> _block = new ActionBlock<IMobileMsg>(async msg =>
{
  try
  {
    Trace.TraceInformation("Sending: {0}", msg.Name);
    await Task.Delay(1000);
    msg.SentTime = DateTime.UtcNow;
    Trace.TraceInformation("X sent at {1}: {0}", msg.Name, msg.SentTime);
  }
  catch (Exception e)
  {
    TraceException(e);
  }
});

这将取代您的整个消费线程和主循环。