Question

I'm creating my own UI handling logic that runs on a single thread. Basically, what I'm doing is

void HandlerMain()
{
    while (true)
    {
        Task[] events = PumpEvents();
        Task.WaitAll(events);
    }
}

where one example task that PumpEvents() returns is

async Task ButtonClick()
{
    var httpClient = new HttpClient();
    string s = await httpClient.GetStringAsync("http://microsoft.com");
    Console.WriteLine(s);
}

The problem of my code is, if one of events takes a long time, it's stuck at Task.WaitAll(), so it can't pump new events making the UI not responsive. Is there any other method than WaitAll() something like

Task[] remainingEvents = PumpEvents();
while (true)
{
    remainingEvents = WaitUntilEveryTasksAwait(remainingEvents);
    remainingEvents.Append(PumpEvents());
}

Maybe I'm on wrong track. I'd appreciate your advice!

Was it helpful?

Solution

@ScottChamberlain No. The built-in UI processor, say WPF, correctly handles async events so whenever the async events do "await", it skips the current context and handles the next events. I want to duplicate this behavior

Based on your comment to me I now understand what your problem is. You need more logic than your simple while(true) loop to be able to process the await messages. The entire system is built up upon the class SynchronizationContext, what you will need to do is derive your own class from SynchronizationContext and override it's methods to queue up your work to be done inside your while loop.

See this article from Stephen Cleary to give you more information on how a Synchronization Context works and posibly some ideas on where to start writing your own.

OTHER TIPS

If I'm understanding, you don't actually need to wait until those tasks are complete to continue. Just remove the call to Task.WaitAll.

For the record, Task.WaitAll is synchronous -- it blocks. You're better off using Task.WhenAll, which returns a task that completes when all of the provided tasks are complete. That lets you await it just like you'd await any of the individual tasks. Just doing that would solve the problem and keep the UI responsive.

E.g.:

async Task HandlerMain()
{
    while (true)
    {
        Task[] events = PumpEvents();
        await Task.WhenAll(events);
    }
}

Of course, in this case, depending on what was calling HandlerMain, you'd still have the problem where PumpEvents was hung up waiting on the long-running task.

This starts to lead down the path of questions like, "Why are you writing your own custom UI message pump when WPF and WinForms have that problem already solved?"

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top