简体   繁体   English

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

[英]Why do awaitable Tasks sometimes block?

I think I have a general misunderstanding of the way the async/await pair works. 我想我对async / await对的工作方式有一般性的误解。 I'm using an EasyNetQ method (an interface for RabbitMQ in C#) and I'm trying to call the following method I created: 我正在使用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);
    }
}

Now the way that I understand this, I should be able to call this method, get a Task from RequestAsync, then do a bunch of stuff and then await that Task once I'm done with that stuff. 现在我理解这一点的方式,我应该能够调用这个方法,从RequestAsync获取一个Task,然后做一堆东西,然后等待那个Task,一旦我完成了那些东西。 Something like this: 像这样的东西:

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;

However, the program blocks on the call to RequestAsync for the timeout duration rather than on the await. 但是,程序会阻止对RequestAsync的调用超时,而不是等待。 Then the await immediately throws a timeout exception. 然后await会立即抛出超时异常。

To see if I was misunderstanding, I tried the following as well: 为了看我是否误解,我也尝试了以下方法:

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

Same thing. 一样。 It blocks on the RequestAsync. 它阻止RequestAsync。 How is that different than a regular blocking synchronous call? 这与常规阻塞同步调用有什么不同?

async does not guarantee that code actually will run asynchronously or will not block the calling thread. async不保证代码实际上将异步运行或不会阻止调用线程。 Ideally it should immediately start operation and return back to caller as you expect, but sometimes (even in existing method of .Net Framework) steps are not completely async. 理想情况下,它应该立即开始操作并按预期返回调用者,但有时(即使在现有的.Net Framework方法中)步骤也不是完全异步。

Sample: 样品:

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. That Sleep always block calling thread. Sleep总是阻止调用线程。 Task to await is returned after first await call in async method. async方法中首次await调用后返回任务到等待。 This is demonstration of the case you are likely observing. 这是您可能观察到的案例的证明。
  2. asynchronous waiting, no thread used for the method at this point. 异步等待,此时没有用于该方法的线程。 Will come back to a thread depending on SynchronizationContext. 将根据SynchronizationContext返回到一个线程。 In most cases - original UI/request thread. 在大多数情况下 - 原始UI /请求线程。
  3. that Sleep need to block some thread. 睡眠需要阻止一些线程。 Depending whether SynchronizationContext setup to return to original thread or not wait may happen on the thread that started the call. 取决于启动调用的线程是否可能发生SynchronizationContext设置以返回原始线程或不等待。

Assuming you're using async/await patterns the way you should, problems like this are usually the fault of the third-party code you use. 假设你正在以你应该的方式使用async/await模式,这样的问题通常是你使用的第三方代码的错误。

The RequestAsync() call you're making gets delegated to the Rpc.Request(...) method code here , which does quite a bit of work before returning a Task. 您正在进行的RequestAsync()调用将被委托给Rpc.Request(...)方法代码 ,这在返回任务之前会做很多工作。 My best guess is that some of that work it does involving Timers ends up blocking, which means that your call to the method itself blocks as well. 我最好的猜测是,它涉及定时器的一些工作最终会阻塞,这意味着你对方法本身的调用也会阻塞。

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

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