1

I have an assembly that runs under an STA thread within a sandbox from a third party, within this thread i have created a duplex WCF client that needs to execute methods on the original STA thread.

The current implementation works fine, within the Duplex callback i obtain the synchronization context of the STA thread as follows and use it to Post back to the STA thread:

private readonly SynchronizationContext _syncContext = AsyncOperationManager.SynchronizationContext;

This all runs within a WinForm initialised in the STA thread, great...but i need to move the WCF duplex proxy so it instead runs under a class instance within the main STA thread. When i remove the winform i end up getting a completely new thread from the above SynchronizationContext.

To clarify:

Winforms:-

  • Start WCF Duplex Proxy on STA thread - ManagedThreadId = 1
  • Receive duplex callback from server - ManagedThreadId = 5
  • Post to callback event method using AsyncOperationManager.SynchronizationContext - ManagedThreadId = 1

Without WinForm (class instance):-

  • Start WCF Duplex Proxy on STA thread - ManagedThreadId = 1
  • Receive duplex callback from server - ManagedThreadId = 6
  • Post to callback event method using AsyncOperationManager.SynchronizationContext - ManagedThreadId = 11

Executing on thread 11 instead of 1 means my methods fail to execute properly within the sandbox, there is no difference in the code between variants other than that one runs under a winform. Does anyone know how i can keep the duplex callback method execution in the main STA thread without using the winform?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Dan Hall
  • 1,474
  • 2
  • 18
  • 43
  • 1
    Can you post your `Main` method? Without it the following diagnosis is supposition. Your title says runs as a Console App and that has two potential sources. The 1st is did you decorate the `Main` Method with [STAThread]? The 2nd and more likely issue is that Console app does not install a synchronization context based on thread type; when a context is created it is an instance of the SynchronizationContext Class and its [Post Method](https://referencesource.microsoft.com/#mscorlib/system/threading/synchronizationcontext.cs,16705ba05372139e,references) use the thread pool. – TnTinMn Mar 18 '19 at 01:50
  • Why do you need an STA in the first place? Do you have COM objects involved? Can you use a form (even invisible) in your console app? If yes, you can use the sync context of this form. – Simon Mourier Mar 18 '19 at 11:47

1 Answers1

5

You're acquiring the synchronization context using the AsyncOperationManager.SynchronizationContext property. That property uses SynchronizationContext.Current under the hood.

That means, the obtained SynchronizationContext depends on the environment you're accessing the property in:

  • the thread you're accessing the property on, and
  • the type of the application.

As you can read in the docs:

The default implementation is the free-threaded implementation.

So, if the current thread's synchronization context is not set, you will get a default free-threaded SynchronizationContext instance. It will Send callbacks by synchronous execution on caller thread and Post callbacks to the ThreadPool (so "random" worker threads will pick them up).

In a Windows Forms app, the main thread's SynchronizationContext will be initialized to a WindowsFormsSynchronizationContext instance for you. That instance will Post the callbacks to the main UI thread.

In a WPF app, this will be a DispatcherSynchronizationContext.

In a console app, there will be no SynchronizationContext for the main thread. Thus, the free-threaded option I mentioned above kicks in, so you get a free-threaded SynchronizationContext instance that posts to the ThreadPool. That pretty much explains the behavior you observe.

If you need that synchronization, you can implement your own thread-affine SynchronizationContext for the main thread of your console app. That is not easy though. In a console app, you have no message loop and no dispatcher that could manage the callback queue. You can take a look on this great answer by Stephen Cleary for an idea of an asynchronous SynchronizationContext. You will need to create a 'main loop' manually though.

dymanoid
  • 14,771
  • 4
  • 36
  • 64