lifestyle
lifestyle

Reputation: 198

How can i keep running thread awaited for some separate callbacks to return, without message-loop and async void?

Suppose we have a wpf window with a button named button1 that handles a void same as this:

void Button1_Clicked()
{ 
   // configuring some new work...

   // awating for this work to be completed:
        while (!_WorkIsCompleted)
        {
            System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(() => { }, System.Windows.Threading.DispatcherPriority.Background);
        }

   // prompting user that this works is completed.
}

And Then Suppose this UI scenario:

  1. User clicks button1 and the void Button1_Clicked calls. Therefore ui thread is wating for an specific work to be completed.

  2. At this moment user Clicks button1 again and we Start a new work (while older work is still operating and is not completed). And we await using message-loop again for this operation to be completed.

  3. The first work is completed. But duo to message-loop architecture, user cannot receive its prompt. Because call stack waits for the later work to be completed, then returns.

  4. Therefore, user receives prompt of later work at first, then receives prompt of sooner work.

In other words, older calls will await next calls for return. But if we use an await keyword within an async void we can make the true result as expected. Because of some architectural and code pattern issues i cannot use tasks in my project. and message-loop cannot make a true result for me.

I know when we use await keyword to call an async void (or function), it tells compiler (probably at compile time) that make some edits on programmer code to supporting await operation.

And it seems that during await, Call Stack is free to make more calls by running thread. With await keyword, we are able to keep running thread awaited for a lot of awaited calls at a same time, that each other is not related to each other. (This behavior is what i want now.)

Actually what is happening in compiled code of an async void, that makes await possible for multiple calls separately, with non-freezing the running thread? It must be possible for me to make a same thing for my situation. How can i edit or change my code to make a same behavior? Is there a solution in c# for this?

Upvotes: 1

Views: 93

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456437

Actually what is happening in compiled code of an async void, that makes await possible for multiple calls separately, with non-freezing the running thread?

await does not create a nested message loop. Instead, await will return (after capturing a "context"). Later, when the awaited object completes, the remainder of that async method will be posted to the context - in this case, a WPF UI context. The main message loop then picks up that message and executes the remainder of the async method. I have an async intro that explains these basic mechanics in more detail.

How can i edit or change my code to make a same behavior?

async/await is a compiler-level transform; it can't be replicated just by code, unless you're willing to do all the transformations yourself (which get messy, especially with loops).

So, the easiest way to make your code work like await is to... use await.

The first step is to replace the boolean _IsCompleted with a signal. In this case, the signal should be asynchronously consumable and will only be triggered once, so the appropriate type is TaskCompletionSource<T> (for any T):

private readonly TaskCompletionSource<object> _CompletedTcs = new TaskCompletionSource<object>();

Instead of setting _IsCompleted to false, you set the signal:

// was: _IsCompleted = false;
_CompletedTcs.SetResult(null);

And then you can just await the resulting task:

// was: while (!_IsCompleted) { ... }
await _CompletedTcs.Task;

Upvotes: 2

Related Questions