To ensure thread-safety, I'm trying to find a generic cross-platform approach to
- execute all delegates asynchronously in the main thread or ...
- execute delegete in a background thread and pass result to the main one
Considering that console apps do not have synchronization context, I create new context when app is loading and then use one of the following methods.
- Set and restore custom SC as described in Await, SynchronizationContext, and Console Apps article by Stephen Toub
- Marshall all delegates to main thread using
context.Post
call as described in the article ExecutionContext vs SynchronizationContext by Stephen Toub - Using background thread with producer-consumer collection as described in Basic synchronization by Joe Albahari
Question
Ideas #1 and #2 set context correctly only if it's done synchronously. If they're called from inside Parallel.For(0, 100)
then synchronization context starts using all threads available in a thread pool. Idea #3 always performs tasks within dedicated thread as expected, unfortunately, not in the main thread. Combining idea #3 with IOCompletionPortTaskScheduler, I can achieve asynchrony and single-threading, unfortunately, this approach will work only in Windows. Is there a way to combine these solutions to achieve requirements at the top of the post, including cross-platform.
Scheduler
public class SomeScheduler
{
public Task<T> RunInTheMainThread<T>(Func<T> action, SynchronizationContext sc)
{
var res = new TaskCompletionSource<T>();
SynchronizationContext.SetSynchronizationContext(sc); // Idea #1
sc.Post(o => res.SetResult(action()), null); // Idea #2
ThreadPool.QueueUserWorkItem(state => res.SetResult(action())); // Idea #3
return res.Task;
}
}
Main
var scheduler = new SomeScheduler();
var sc = SynchronizationContext.Current ?? new SynchronizationContext();
new Thread(async () =>
{
var res = await scheduler.ExecuteAsync(() => 5, sc);
});