Search code examples
c#winformsmultithreadingsynchronizationcontext

Framework like thread marshalling


I always thought I could use SynchronizationContext to marshal a call to another thread. Apparently I was wrong as SyncCtx.Send() doesn't do anything but invoking the given delegate (stays on the same thread). Do I really need to derive from SynchronizationContext and do work on the thread context? I feel like missing something.

What I want to achive: Imagine a little API for executing commands within an app. You can also execute a command on a background thread, as you can assign a delegate to run when the command has finished execution. This "Call-me-when-done"-Delegate gets one single paramter (State) containing success/failure flag, optional exception information, etc. I want to call this delegate on the original calling thread so that devs using the lib do not need to handle invoke required etc. I would just like to take this away and let them do simple non-thread-aware programming. WindowsFormsSynchronizationContext doesn't seem to help either if you don't give it some control as a target.

Thanks for any help!


Solution

  • When you are in winforms and you use the synchronizationcontext the call will be marshalled to the GUI thread.

    for your specific case I guess something like this should work, probably it will be a good idea to create a class that represents a Command

    public class CommandManager
    {
        private readonly SynchronizationContex _synchronizationContex;
    
        public CommandManager(SynchronizationContext synchronizationContex)
        {
            _synchronizationContex = synchronizationContex;
        }
    
        public void ExecuteAsync(Func<State> action, Action<State> callback)
        {
            ThreadPool.QueueUserWorkItem(o => {
                                                 state = action();
                                                 _synchronizationContex.Send(oo => callback(state));
                                              });
        } 
    }
    

    you'd create like this (in the GUI thread, so for example in your main form)

    var commandManager = new CommandManager(SynchronizationContext.Current);
    

    you'd use it like this:

    commandManager.ExecuteAsync(() => new State() { Success = true }, 
                                 c => MessageBox.Show("success in the GUI thread"));