简体   繁体   English

在使用BlockingCollection作为队列的使用者中等待异步方法

[英]awaiting async method in consumer using BlockingCollection as queue

I am working on a server side console application which receives data from multiple WCF services, does some work on it and then forwards the results to an IIS server over a single connection using SignalR. 我正在一个服务器端控制台应用程序上工作,该应用程序从多个WCF服务接收数据,对其进行一些处理,然后将结果通过使用SignalR的单个连接转发到IIS服务器。

I tried to implement this using the producer consumer pattern where the WCF services are the producers and the class sending the data using SignalR is the consumer. 我尝试使用生产者使用者模式来实现这一点,其中WCF服务是生产者,而使用SignalR发送数据的类是使用者。 For the queue I used the BlockingCollection . 对于队列,我使用了BlockingCollection

However, when using await/async to send the data in the consumer's while loop gets stuck until all other threads have finished adding data to the queue. 但是,当使用await / async在使用者的while循环中发送数据时,它会阻塞,直到所有其他线程都已将数据添加到队列中为止。

For testing purposes I have replaced the code actually sending the data with a Task.Delay(1000).Wait(); 为了进行测试,我将实际发送数据的代码替换为Task.Delay(1000).Wait(); or await Task.Delay(1000); await Task.Delay(1000); which both get stuck as well. 两者都会卡住。 A simple Thread.Sleep(1000); 一个简单的Thread.Sleep(1000); seems to work just fine, leading me to think the asynchronous code is the problem. 似乎工作正常,这使我认为异步代码是问题。

So my question is: Is there something preventing the asynchronous code being completed in the while loop? 所以我的问题是:是否有某些因素可以防止在while循环中完成异步代码? What am I missing? 我想念什么?

I'm starting the consumer thread like this: 我正在像这样启动使用者线程:

new Thread(Worker).Start();

And the consumer code: 和消费者代码:

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

As spender correctly pointed out, BlockingCollection (as the name implies) is intended only for use with blocking code, and does not work so well with asynchronous code. 正如支出者正确指出的那样, BlockingCollection (顾名思义)仅用于阻塞代码,不适用于异步代码。

There are async-compatible producer/consumer queues, such as BufferBlock<T> . 有异步兼容的生产者/消费者队列,例如BufferBlock<T> In this case, I would think ActionBlock<T> would be even better: 在这种情况下,我认为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);
  }
});

This replaces your entire consuming thread and main loop. 这将替换您的整个消耗线程和主循环。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM