Say you\'re writing a custom single threaded GUI library (or anything with an event loop). From my understanding, if I use async/await
, or just regular TPL cont
Implementing a custom SynchronizationContext
is not the easiest thing in the world. I have an open-source single-threaded implementation here that you can use as a starting point (or possibly just use in place of your main loop).
By default, AsyncContext.Run
takes a single delegate to execute and returns when it is fully complete (since AsyncContext
uses a custom SynchronizationContext
, it is able to wait for async void
methods as well as regular async/sync code).
AsyncContext.Run(async () => await DoSomethingAsync());
If you want more flexibility, you can use the AsyncContext
advanced members (these do not show up in IntelliSense but they are there) to keep the context alive until some external signal (like "exit frame"):
using (var context = new AsyncContext())
{
// Ensure the context doesn't exit until we say so.
context.SynchronizationContext.OperationStarted();
// TODO: set up the "exit frame" signal to call `context.SynchronizationContext.OperationCompleted()`
// (note that from within the context, you can alternatively call `SynchronizationContext.Current.OperationCompleted()`
// Optional: queue any work you want using `context.Factory`.
// Run the context; this only returns after all work queued to this context has completed and the "exit frame" signal is triggered.
context.Execute();
}
AsyncContext
's Run
and Execute
replace the current SynchronizationContext
while they are running, but they save the original context and set that as current before returning. This allows them to work nicely in a nested fashion (e.g., "frames").
(I'm assuming by "frame" you mean a kind of WPF-like dispatcher frame).