Get SynchronizationContext from a given Thread
Asked Answered
O

5

7

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 = SynchronizationContext.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?

Outfox answered 5/11, 2010 at 15:41 Comment(3)
At a later time (after the WinForms implementation has loaded), you could get the UI thread's synchronization context like this: (untested) var context = (SynchronizationContext)someUiControl.Invoke(new Func<SynchronizationContext>(() => SynchronizationContext.Current)); and cache it for later use.Commence
@Heinzi: Looks creative. I would need a control for that however, which is even worse than needing the SynchronizationContext object.Outfox
Dunno why but my link to this question from mine "Is the phrase from a book “The current SynchronizationContext is a property of the current thread” correct"?" did not appear in Related or Linked sections on the right sidebar, so I put it in this comment...so, it has appeared thereafterManaker
A
16

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.

Abbotson answered 5/11, 2010 at 15:59 Comment(3)
Concerning the missing someThread.SynchronizationContext property, it would still be appreciated if a Thread object provided that property. It's notion is "what would SynchronizationContext.Current" give me if I was that certain thread? For threads which don't have a context, it would return null just as SynchronizationContext.Current would do.Outfox
Apart from that, thanks for the informative answer. Like Jon and you suggested I placed that in a Load event handler.Outfox
Reed, I asked subquestion "Is the phrase from a book “The current SynchronizationContext is a property of the current thread” correct"?"Manaker
R
7

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.

Raja answered 17/4, 2011 at 12:11 Comment(0)
B
7

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"

Bank answered 29/4, 2013 at 2:11 Comment(1)
+1 Thanks. Although it doesn't answer my original question directly your answer still adds some valuable information.Outfox
E
2

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?

Effeminate answered 5/11, 2010 at 15:48 Comment(2)
Did that, wasn't completely happy with having that line of code "in the middle of the code" - I use Application.Run() twice, first for an splash screen and then for the main form, so it's now placed in the splash screen's Load event - but actually: As long as it's there, it works. If the splash screen ever happened to be removed, I'd immediately notice it because the property would raise a NullPointerException.Outfox
Jon, I've asked the follow-up "Is the phrase from a book “The current SynchronizationContext is a property of the current thread” correct"?"Manaker
M
1

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:

ldarg.0<br>ldfld SynchronizationContext _syncContext/ExecutionContext<br>ret

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!

Mirepoix answered 8/7, 2017 at 12:59 Comment(3)
What you are basically saying is a thread has an execution context and this has a private field that is a synchronization context and it can be accessed by reflection. It turns out that this is true. However it is not true is that this synchronization context invokes back onto the thread from which you retrieved it, which was the property it needed to have. So not only is your answer very hard to decipher, it is also wrong. Sorry for sounding slightly arsey, but i wanted to clarify my downvote.Friedrich
@briantyler The code works as advertised to get the SynchronizationContext for the Thread specified by the caller, if it has one (and critically on WPF, does not silently demand one if it doesn't, which is the real use here). It's true that the operation is somewhat vacuous if the caller specifies Thread.CurrentThread.Mirepoix
The point is Glen that you get a SynchronizationContext, but you don't actually get one for the thread specified for the caller. That context doesn't invoke onto the specified thread, which is what you would want it to do, so it is less than useless.Friedrich

© 2022 - 2024 — McMap. All rights reserved.