I have following code:
[TestMethod]
public void StartWorkInFirstThread()
{
if (SynchronizationContext.Current == null)
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext());
var syncContext = SynchronizationContext.Current;
Console.WriteLine("Start work in the first thread ({0})",
Thread.CurrentThread.ManagedThreadId);
var action = ((Action) DoSomethingInSecondThread);
action.BeginInvoke(CallbackInSecondThread, syncContext);
// Continue its own work
}
private static void DoSomethingInSecondThread()
{
Console.WriteLine("Do something in the second thread ({0})",
Thread.CurrentThread.ManagedThreadId);
}
private void CallbackInSecondThread(IAsyncResult ar)
{
Console.WriteLine("Callback in the second thread ({0})",
Thread.CurrentThread.ManagedThreadId);
var syncContext = (SynchronizationContext) ar.AsyncState;
syncContext.Post(CallbackInFirstThread, null);
}
private void CallbackInFirstThread(object obj)
{
Console.WriteLine("Callback in the first thread ({0})",
Thread.CurrentThread.ManagedThreadId);
}
I expect last method to be executed in the first thread, i.e. initial thread where SynchronizationContext is taken from, because I call Post()
method of this context. I.e. something like this:
Start work in the first thread (28)
Do something in the second thread (17)
Callback in the second thread (17)
Callback in the first thread (28)
Isn't it the meaning of SynchronizationContext? But actually I have following output:
Start work in the first thread (28)
Do something in the second thread (17)
Callback in the second thread (17)
Callback in the first thread (7)
What is the problem? Does something go wrong with SynchronizationContext or I have some misunderstanding?
Update: I call this method as a unit test using Resharper test runner.
Your expectation is wrong because there's no general way to "inject" a delegate into a running thread. Your "first thread" was started in the test runner, will execute one or more tests, and will then stop - there's no way to interrupt it and tell it to run
CallbackInFirstThread
. TheSynchronizationContext
class runsPost
-ed delegates in the thread pool because that's about the only option it has.Derived classes like
WindowsFormsSynchronizationContext
make use of the message loop in WinForms applications to pass thePost
-ed delegate to the UI thread, but there's no equivalent in a test runner.If you want to check which
SynchronizationContext
the code you're testing is using, you could create your own derived class that sets a flag you can check in your test. Here's an example:In
StartWorkInFirstThread
, set the context to an instance ofTestSynchronizationContext
:After you call
BeginInvoke
, you need to wait for thePost
to happen before you exit the test, so call:In
CallbackInFirstThread
you can check what context is being used with something like:The point is that there's no easy way to actually post back to the first thread, but you can check that the right context is being used so that, when your code runs in a real application, the callback will be running in the UI thread.
Default implementation of SynchronizationContext just executes passed delegate in the calling thread (in the thread that invokes Send/Post method not the thread that captures context). If you need some particular behavior, like thread affinity for some operations, you should implement this manually. BCL contains few out-of-box implementations for simplification of UI interoperability, like WindowsFormsSynchronizationContext or DispatcherSynchronizationContext.
See http://www.codeproject.com/KB/threads/SynchronizationContext.aspx
There is the answer you need. You must override
SynchronizationContext
to make it properly handling your operations.Read starting from: