views:

68

answers:

1

So I'm writing a bunch or multithreaded code in library that will run in the background. So I'm having the UI pass in SynchronizationContext object so I can Post events back to the main UI thread.

Many different objects have public events the UI can subscribe to. I initially just add the SyncContext object as a parameter to the creation call the sort of initializing my library. Once passed in I store it in a static variable on a static class thus making it globally accessible (although it is internal).

I like this because anytime I need to make some bit of code run on the UI thread I can easily do it without modifing much code. But it means that alot of my code has a dependancy on the Static class that isn't explicit. Making the dependency explicit would require alot of code change to add it to the constructor of any class that uses the variable, storing a reference to it in any class that even creates objects that need it.

So i have option

A) static variable that hides dependencies for my SynchronizationContext

B) nearly every class knows about and passes around my SynchronizationContext.

Is there another pattern that would be better for this sort of situation?

.net framwork 3.5. The main UI is going to be WPF, but we want it to work with winforms as well.

+1  A: 

If you know that your library will only ever be used from a single synchronization context, then I see no reason why you can't capture the synchronization context using a static member (your option A).

Why do you see a need to make this dependency 'explicit'?

Where option A could potentially be problematic is if your library may be used from multiple synchronization contexts at the same time. (For example multiple UI windows running on their own thread and message pump). This is fairly unusual though - mostly you have a single UI thread and a single synchronization context.

Also, if an application wishes to handle some events without forcing serialization to the UI thread (via the sync context) it cannot do so.

If these issues are not a problem for your library, then option A should be viable.

Edit - in response to comment:

Joel, I'm not sure if this is helpful given what you describe, but one thing you might want to consider if you sometimes want to use an explicit synchronization context would be to use thread-local storage to store the parameter without needing to create overrides of the method to take the parameter.

For example, I have in the past myself needed to allow callers to my APIs to both use the default synchronization context for the current calling thread but also to explicitly set a different synchronization context. One approach I could have taken would have been to provide overloads of my API methods that allow passing a synchronization context parameter. In my case, this would have been pretty ugly since it would have meant creating a lot of method overloads for a fairly infrequent use case.

So, what I did instead was create a class that handled creating a 'scope' during which the current sync context gets overridden (thereby effectively passing it to the called methods). The class takes care of (in order) 1) caching the current sync context, 2) setting the current context to a sync context specified by the caller, and 3) resetting the sync context at the end of the 'scope'.

Here's what it looks like:

public class SyncContextScope : IDisposable
{
    SynchronizationContext m_contextOnEntry;

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="useContext"></param>
    public SyncContextScope(SynchronizationContext useContext)
    {
        m_contextOnEntry = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(useContext);
    }

    #region IDisposable Members

    void IDisposable.Dispose()
    {
        SynchronizationContext.SetSynchronizationContext(m_contextOnEntry);
    }

    #endregion
}

And you use it like so:

using(new SyncContextScope(yourSyncContext))
{
   yourApi.CallSomeMethod();
}

Your API's method (in the above example CallSomeMethod) can now make use of the SynchronizationContext.Current (which uses TLS to store a sync context). All without having to resort to providing a sychronizationcontext parameter.

Phil
I was mainly thinking of things like unit tests, where you can spin up a class that's labeled internal or private. So where normally you'd have had to pass in the SyncContext to get this far, instead it could be null, then maybe the test works, maybe it works if it's run after a different test, ect... I guess that's what it boils down to. If you want easier to write code use the static member. If you want easier to test code pass it around.
Joel Barsotti