开发者

Get SynchronizationContext from a given Thread

I can't work out how to get the SynchronizationContext of a given Thread:

Thread uiThread = UIConfiguration.UIThread;
SynchronizationContext context = uiThread.Huh?;

Why would I need that?

Because I need to post to the UIThread from different spots all across the front end application. So I defined a static property in a class called UIConfiguration. I set this property in the Program.Main method:

UIConfiguration.UIThread = Thread.CurrentThread;

In that very moment I can be sure I have the right thread, however I cannot set a static property like

UIConfiguration.SynchronizationContext = SynchronizationCon开发者_C百科text.Current

because the WinForms implementation of that class has not yet been installed. Since each thread has it's own SynchronizationContext, it must be possible to retrieve it from a given Thread object, or am I completely wrong?


This is not possible. The problem is that a SynchronizationContext and a Thread are really two completely separate concepts.

While it's true that Windows Forms and WPF both setup a SynchronizationContext for the main thread, most other threads do not. For example, none of the threads in the ThreadPool contain their own SynchronizationContext (unless, of course, you install your own).

It's also possible for a SynchronizationContext to be completely unrelated to threads and threading. A synchronization context can easily be setup that synchronizes to an external service, or to an entire thread pool, etc.

In your case, I'd recommend setting your UIConfiguration.SynchronizationContext within the initial, main form's Loaded event. The context is guaranteed to be started at that point, and will be unusable until the message pump has been started in any case.


I know this is an old question, and apologize for the necro, but I just found a solution to this problem that I figured might be useful for those of us who have been googling this (and it doesn't require a Control instance).

Basically, you can create an instance of a WindowsFormsSynchronizationContext and set the context manually in your Main function, like so:

    _UISyncContext = new WindowsFormsSynchronizationContext();
    SynchronizationContext.SetSynchronizationContext(_UISyncContext);

I have done this in my application, and it works perfectly without issues. However, I should point out that my Main is marked with STAThread, so I am not sure if this will still work (or if it's even necessary) if your Main is marked with MTAThread instead.

EDIT: I forgot to mention it, but _UISyncContext is already defined at the module level in the Program class in my application.


I found as the most concise and helpful to me the following passages from book by Alex Davies "Async in C# 5.0. O'Reilly Publ., 2012", p.48-49:

  • "SynchronizationContext is a class provided by the .NET Framework, which has the ability to run code in a particular type of thread.
    There are various Synchronization Contexts used by .NET, the most important of which are the UI thread contexts used by WinForms and WPF."

  • "Instances of SynchronizationContext itself don’t do anything very useful, so all actual instances of it tend to be subclasses.

    It also has static members which let you read and control the current SynchronizationContext.

    The current SynchronizationContext is a property of the current thread.

    The idea is that at any point that you’re running in a special thread, you should be able to get the current SynchronizationContext and store it. Later, you can use it to run code back on the special thread you started on. All this should be possible without needing to know exactly which thread you started on, as long as you can use the SynchronizationContext, you can get back to it.

    The important method of SynchronizationContext is Post, which can make a delegate run in the right context"
    .

  • "Some SynchronizationContexts encapsulate a single thread, like the UI thread.
    Some encapsulate a particular kind of thread — for example, the thread pool — but can choose any of those threads to post the delegate to. Some don’t actually change which thread the code runs on, but are only used for monitoring, like the ASP.NET Synchronization Context"


I don't believe that every thread does have its own SynchronizationContext - it just has a thread-local SynchronizationContext.

Why don't you just set UIConfiguration.UIThread in the Loaded event of your form, or something similar?


Complete and working extension methods for getting the SynchronizationContext from a Thread or ExecutionContext (or null if none is present), or a DispatcherSynchronizationContext from a Dispatcher. Tested on .NET 4.6.2.

using Ectx = ExecutionContext;
using Sctx = SynchronizationContext;
using Dctx = DispatcherSynchronizationContext;

public static class _ext
{
    // DispatcherSynchronizationContext from Dispatcher
    public static Dctx GetSyncCtx(this Dispatcher d) => d?.Thread.GetSyncCtx() as Dctx;

    // SynchronizationContext from Thread
    public static Sctx GetSyncCtx(this Thread th) => th?.ExecutionContext?.GetSyncCtx();

    // SynchronizationContext from ExecutionContext
    public static Sctx GetSyncCtx(this Ectx x) => __get(x);

    /* ... continued below ... */
}

All of the above functions end up calling the __get code shown below, which warrants some explanation.

Note that __get is a static field, pre-initialized with a discardable lambda block. This allows us to neatly intercept the first caller only, in order to run the one-time initialization, which prepares a tiny and permanent replacement delegate that's much faster and reflection-free.

The final act for the intrepid initialization effort is to swap the replacement into '__get', which simultaneously and tragically means the code discards itself, leaving no trace, and all subsequent callers proceed directly into the DynamicMethod proper without even a hint of bypass logic.

static Func<Ectx, Sctx> __get = arg =>
{
    // Hijack the first caller to do initialization...

    var fi = typeof(Ectx).GetField(
        "_syncContext",                         // private field in 'ExecutionContext'
        BindingFlags.NonPublic|BindingFlags.Instance);

    var dm = new DynamicMethod(
        "foo",                                  // (any name)
        typeof(Sctx),                           // getter return type
        new[] { typeof(Ectx) },                 // type of getter's single arg
        typeof(Ectx),                           // "owner" type
        true);                                  // allow private field access

    var il = dm.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Ldfld, fi);
    il.Emit(OpCodes.Ret);

    // ...now replace ourself...
    __get = (Func<Ectx, Sctx>)dm.CreateDelegate(typeof(Func<Ectx, Sctx>));

    // oh yeah, don't forget to handle the first caller's request
    return __get(arg);                 //  ...never to come back here again. SAD!
};

The cute part is the very end where--in order to actually fetch the value for the pre-empted first caller--the function ostensibly calls itself with its own argument, but avoids recursing by replacing itself immediately prior.

There's no particular reason for demonstrating this unusual technique on the particular problem of SynchronizationContext under discussion on this page. Grabbing the _syncContext field out of an ExecutionContext could be easily and trivially addressed with traditional reflection (plus some extension method frosting). But I thought I'd share this approach which I've personally used for quite a while now, because it is also easily adapted and just as widely applicable to such cases.

It's especially appropriate when extreme performance is necessary in accessing the non-public field. I think I originally used this in a QPC-based frequency counter where the field was read in a tight loop that iterated every 20 or 25 nanoseconds, which wouldn't really be possible with conventional reflection.

This concludes the main answer, but below I've included some interesting points, less relevant to the questioner's inquiry, moreso to the technique just demonstrated.

 


Runtime callers

For clarity, I separated the "installation swap" and "first usage" steps into two separate lines in the code shown above, as opposed to what I have in my own code (the following version also avoids one main-memory fetch versus the previous, potentially implicating thread-safety, see detailed discussion below):

return (__get = (Func<Ectx, Sctx>)dm.CreateDel...(...))(arg);

In other words, all callers, including the first, fetch the value in exactly the same way, and no reflection code is ever used to do so. It only writes the replacement getter. Courtesy of il-visualizer, we can see the body of that DynamicMethod in the debugger at runtime:

Get SynchronizationContext from a given Thread

Lock-free thread safety

I should note that swapping in the function body is a fully thread-safe operation given the .NET memory model and the lock-free philosophy. The latter favors forward-progress guarantees at the possible expense of doing duplicate or redundant work. Multi-way racing to initialize is correctly permitted on a fully sound theoretical basis:

  • the race entry point (initialization code) is globally pre-configured and protected (by the .NET loader) so that (multiple) racers (if any) enter the same initializer, which can never be seen as null.
  • multiple race products (the getter) are always logically identical, so it doesn't matter which one any particular racer (or later non-racing caller) happens to pick up, or even whether any racer ends up using the one they themselves produced;
  • each installation swap is a single store of size IntPtr, which is guaranteed to be atomic for any respective platform bitness;
  • finally, and technically absolutely critical to perfect formal correctness, work products of the "losers" are reclaimed by GC and thus do no leak. In this type of race, losers are every racer except the last finisher (since everyone else's efforts get blithely and summarily overwritten with an identical result).

Although I believe these points combine to fully protect the code as written under every possible circumstance, if you're still suspicious or wary of the overall conclusion, you can always add an extra layer of bulletproofing:

var tmp = (Func<Ectx, Sctx>)dm.CreateDelegate(typeof(Func<Ectx, Sctx>));
Thread.MemoryBarrier();
__get = tmp;
return tmp(arg);

It's just a paraniod version. As with the earlier condensed one-liner, the .NET memory model guarantees that there is exactly one store--and zero fetches--to location of '__get'. (The full expanded example at the top does do an extra main memory fetch, but is still sound thanks to the second bullet point) As I mentioned, none of this should be necessary for correctness, but it could, in theory, give a miniscule performance bonus: by conclusively ending the race earlier, the aggressive flush could, in an extremely rare case, prevent a subsequent caller on a dirty cache line from unnecessarily (but again, harmlessly) racing.

Double-thunking

Calls into the final, hyper-fast method are still thunked through the static extension methods shown earlier. This is because we also need to somehow represent entry point(s) that actually exist at compile time for the compiler to bind against and propagate metadata for. The double-thunk is a small price to pay for the overwhelming convenience of strongly-typed metadata and intellisense in the IDE for customized code that can't actually be resolved until runtime. Yet it runs at least as fast as statically compiled code, way faster that doing a bunch of reflection on every call, so we get the best of both worlds!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜