Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'await' does not return, when my Task is started from a custom TaskScheduler

Background:

I have a "Messenger" class. It sends messages. But due to limitations, let's say it can only send - at most - 5 messages at a time.

I have a WPF application which queues messages as needed, and waits for the queued message to be handled before continuing. Due to the asynchronous nature of the application, any number of messages could be awaited at any given time.

Current Implementation:

To accomplish this, I've implemented a Task<Result> SendMessage(Message message) API within my messaging class. Internal to the messaging class is a custom TaskScheduler (the LimitedConcurrencyTaskScheduler from MSDN), with its concurrency level set to 5. In this way, I would expect that no matter how many messages are queued, only 5 will be sent out at a time, and my client application will patiently wait until its respective message has been handled.

Problem:

When I await the SendMessage method, I can see via the debugger that the message was completed and the result returned, but my code never executes beyond the awaited method call!

Is there some special considerations that need to be made, when awaiting a Task which was scheduled using a different TaskScheduler?

Snipped Code:

From my client/consuming function:

public async Task Frobulate()
{
    Message myMessage = new Message(x, y, z);
    await messenger.SendMessage(myMessage);

    //Code down here never executes!
}

From my messenger class:

private TaskScheduler _messengerTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(5);
private TaskFactory _messengerTaskFactory = new TaskFactory(_messengerScheduler);

public Task<Result> SendMessage(Message message)
{
    //My debugger has verified that "InternalSendMessage" has completed, 
    //but the caller's continuation appears to never execute
    return _messengerTaskFactory.StartNew(() => InternalSendMessage(message));
}

Update:

The 'freeze' does not actually appear to be caused by my custom TaskScheduler; when I queue up the Task with the default TaskFactory, the same behavior occurs! There must be something else happening at a more fundamental level, likely due to my own stupidity.

like image 995
BTownTKD Avatar asked Jun 13 '14 21:06

BTownTKD


1 Answers

Based on the comments, you probably have a deadlock because you're blocking on async code.

When using async, whenever there are thread restrictions on the SynchronizationContext or TaskScheduler and the code blocks using Task.Result or Task.Wait there's a possibility of deadlocking. The asynchronous operation needs a thread to finish execution, which it can't get because the SynchronizationContext (or TaskScheduler in your case) is waiting for that same exact operation to complete before allowing "new" ones to run.

Go deeper in Stephen Cleary's blog post: Don't Block on Async Code

like image 97
2 revs, 2 users 93% Avatar answered Oct 20 '22 23:10

2 revs, 2 users 93%