views:

44

answers:

2

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.

+1  A: 

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.

desco
So I need "just execute passed delegate in the calling thread"
Alex Kofman
But It does not, It executes it in another thread.
Alex Kofman
+1  A: 

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:

Notice that DoWork is executed on thread 11, the same thread as Run1. Not much of a SynchronizationContext into the main thread. Why? What's going on? Well... This is the part when you realize that nothing is for free in life. Threads can't just switch contexts between them, they must have an infrastructure built-in into them in order to do so. The UI thread, for example, uses a message pump, and within its SynchronizationContext, it leverages the message pump to sync into the UI thread.

Alex Yakunin
Send simply calls the delegate on the calling thread (no thread switching of any kind), and Post does the same thing, but simply uses the ThreadPool to do it in an async fashion. In my opinion, this class should be abstract. The default implementation of this class is confusing and useless.
Alex Kofman
^^^ It's a quote from this article...
Alex Kofman
Absolutely agree.
Alex Yakunin