Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do awaitable Tasks sometimes block?

I think I have a general misunderstanding of the way the async/await pair works. I'm using an EasyNetQ method (an interface for RabbitMQ in C#) and I'm trying to call the following method I created:

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. 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. Then the await immediately throws a timeout exception.

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. How is that different than a regular blocking synchronous call?

like image 708
C. Williamson Avatar asked Mar 26 '26 19:03

C. Williamson


1 Answers

async does not guarantee that code actually will run asynchronously or will not block the calling thread. 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.

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. Task to await is returned after first await call in async method. 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. In most cases - original UI/request thread.
  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.
like image 146
Alexei Levenkov Avatar answered Mar 28 '26 10:03

Alexei Levenkov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!