为什么等待任务有时会阻止?

时间:2017-06-09 17:31:47

标签: c# async-await easynetq

我认为我对async / await对的工作方式有一般性的误解。我正在使用EasyNetQ方法(C#中的RabbitMQ接口),我正在尝试调用我创建的以下方法:

public Task<U> RequestDirectReply<T, U>(T request, int timeout) where T : class where U : class
{
    using (var bus = RabbitHutch.CreateBus($"virtualHost=MyVirtualHost;timeout={timeout};host=MyHostName"))
    {
        return bus.RequestAsync<T, U>(request);
    }
}

现在我理解这一点的方式,我应该能够调用这个方法,从RequestAsync获取一个Task,然后做一堆东西,然后在完成那些东西后等待那个Task。像这样:

Task<Reply> task = RequestDirectReply<Request, Reply>(msg, 10);

for (int i = 0; i < 1000000000; ++i)
{
    // Hi, I'm counting to a billion
}

var reply = await task;

但是,程序会阻止对RequestAsync的调用超时,而不是等待。然后await会立即抛出超时异常。

为了看我是否误解,我也尝试了以下内容:

public async Task<U> RequestDirectReply<T, U>(T request, int timeout) where T : class where U : class
{
    using (var bus = RabbitHutch.CreateBus($"virtualHost=MyVirtualHost;timeout={timeout};host=MyHostName"))
    {
        return await bus.RequestAsync<T, U>(request);
    }
}

同样的事情。它阻止RequestAsync。这与常规阻塞同步调用有什么不同?

2 个答案:

答案 0 :(得分:7)

async不保证代码实际上将异步运行或不会阻止调用线程。理想情况下,它应该立即开始操作并按预期返回调用者,但有时(即使在现有的.Net Framework方法中)步骤也不是完全异步。

样品:

async Task<int> MyAsync()
{
     Thread.Sleep(1000); // (1) sync wait on calling thread
     await Task.Delay(1000); // (2) async wait off calling thread
     Thread.Sleep(1000); // (3) sync wait likely on original thread
}
  1. Sleep始终阻止调用线程。在await方法中首次async调用后,将返回等待任务。这是对您可能观察到的案例的证明。
  2. 异步等待,此时没有用于该方法的线程。将根据SynchronizationContext返回到一个线程。在大多数情况下 - 原始UI /请求线程。
  3. 睡眠需要阻止一些线程。取决于启动调用的线程是否可能发生SynchronizationContext设置以返回原始线程或不等待。

答案 1 :(得分:3)

假设您正在使用async/await模式,那么这类问题通常是您使用的第三方代码的错误。

您正在进行的RequestAsync()调用会被委托给Rpc.Request(...)方法code here,这在返回任务之前会做很多工作。我最好的猜测是它涉及定时器的一些工作最终会阻塞,这意味着你对方法本身的调用也会阻塞。