上一篇中已经讲了SynchronizationContext 的一些内容,现在让我们更加深入地去了解它!
     继上篇中的问题"在UI线程上对SynchronizationContext的使用,可以适用于其他线程呢?"
     OK,我们把它放置在非UI线程上,这是你用SynchronizationContext.Current的属性来获取,你会发现你得到的是null,这时候,你可能会说,既然它不存在,那么我自己创建一个SynchronizationContext对象,这样就没问题了吧!?可是,最后它并不会像UI线程中那样去工作。
    让我们看下面的例子:
class Program
{
    
private static SynchronizationContext mT1 = null;

    
static void Main(string[] args)
    {
        
// log the thread id
        int id = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine(
"Main thread is " + id);

        
// create a sync context for this thread
        var context = new SynchronizationContext();
        
// set this context for this thread.
        SynchronizationContext.SetSynchronizationContext(context);

        
// create a thread, and pass it the main sync context.
        Thread t1 = new Thread(new ParameterizedThreadStart(Run1));
        t1.Start(SynchronizationContext.Current);
        Console.ReadLine();
    }

    
static private void Run1(object state)
    {
        
int id = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine(
"Run1 Thread ID: " + id);

        
// grab  the sync context that main has set
        var context = state as SynchronizationContext;

        
// call the sync context of main, expecting
        
// the following code to run on the main thread
        
// but it will not.
        context.Send(DoWork, null);

        
while (true)
            Thread.Sleep(
10000000);
    }

    
static void DoWork(object state)
    {
        
int id = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine(
"DoWork Thread ID:" + id);
    }
}
输出的结果:
Main thread is 10
Run1 Thread ID: 
11
DoWork Thread ID:
11
     注意上面的输出结果,DoWork和Run1是运行在同一线程中的,SynchronizationContext并没有把DoWork带入到主线程中执行,为什么呢?!
我们可以先看SynchronizationContext的原码(SynchronizationContext原代码):
 System.Threading
{
    using Microsoft.Win32.SafeHandles;
    
using System.Security.Permissions;
    
using System.Runtime.InteropServices;
    
using System.Runtime.CompilerServices;
    
using System.Runtime.ConstrainedExecution;
    
using System.Reflection;

    
internal struct SynchronizationContextSwitcher : IDisposable
    {
        
internal SynchronizationContext savedSC;
        
internal SynchronizationContext currSC;
        
internal ExecutionContext _ec;

        
public override bool Equals(Object obj)
        {
            
if (obj == null || !(obj is SynchronizationContextSwitcher))
                
return false;
            SynchronizationContextSwitcher sw 
= (SynchronizationContextSwitcher)obj;
            
return (this.savedSC == sw.savedSC &&
                    
this.currSC == sw.currSC && this._ec == sw._ec);
        }

        
public override int GetHashCode()
        {
            
return ToString().GetHashCode();
        }

        
public static bool operator ==(SynchronizationContextSwitcher c1,
                                       SynchronizationContextSwitcher c2)
        {
            
return c1.Equals(c2);
        }

        
public static bool operator !=(SynchronizationContextSwitcher c1,
                                       SynchronizationContextSwitcher c2)
        {
            
return !c1.Equals(c2);
        }

        
void IDisposable.Dispose()
        {
            Undo();
        }

        
internal bool UndoNoThrow()
        {
            
if (_ec  == null)
            {
                
return true;
            }

            
try
            {
                Undo();
            }
            
catch
            {
                
return false;
            }
            
return true;
        }

        
public void Undo()
        {
            
if (_ec  == null)
            {
                
return;
            }

            ExecutionContext  executionContext 
=
              Thread.CurrentThread.GetExecutionContextNoCreate();
            
if (_ec != executionContext)
            {
                
throw new InvalidOperationException(Environment.GetResourceString(
                          
"InvalidOperation_SwitcherCtxMismatch"));
            }
            
if (currSC != _ec.SynchronizationContext)
            {
                
throw new InvalidOperationException(Environment.GetResourceString(
                          
"InvalidOperation_SwitcherCtxMismatch"));
            }
            BCLDebug.Assert(executionContext 
!= null" ExecutionContext can't be null");
            
// restore the Saved Sync context as current
            executionContext.SynchronizationContext = savedSC;
            
// can't reuse this anymore
            _ec = null;
        }
    }

    
public delegate void SendOrPostCallback(Object state);

    [Flags]
    
enum SynchronizationContextProperties
    {
        None 
= 0,
        RequireWaitNotification 
= 0x1
    };

    
public class SynchronizationContext
    {
        SynchronizationContextProperties _props 
= SynchronizationContextProperties.None;

        
public SynchronizationContext()
        {
        }

        
// protected so that only the derived sync
        
// context class can enable these flags
        protected void SetWaitNotificationRequired()
        {
            
// Prepare the method so that it can be called
            
// in a reliable fashion when a wait is needed.
            
// This will obviously only make the Wait reliable
            
// if the Wait method is itself reliable. The only thing
            
// preparing the method here does is to ensure there
            
// is no failure point before the method execution begins.

            RuntimeHelpers.PrepareDelegate(
new WaitDelegate(this.Wait));
            _props 
|= SynchronizationContextProperties.RequireWaitNotification;
        }

        
public bool IsWaitNotificationRequired()
        {
            
return ((_props &
              SynchronizationContextProperties.RequireWaitNotification) 
!= 0);
        }

        
public virtual void Send(SendOrPostCallback d, Object state)
        {
            d(state);
        }

        
public virtual void Post(SendOrPostCallback d, Object state)
        {
            ThreadPool.QueueUserWorkItem(
new WaitCallback(d), state);
        }

        
public virtual void OperationStarted()
        {
        }

        
public virtual void OperationCompleted()
        {
        }

        
// Method called when the CLR does a wait operation
        public virtual int Wait(IntPtr[] waitHandles,
                       
bool waitAll, int millisecondsTimeout)
        {
            
return WaitHelper(waitHandles, waitAll, millisecondsTimeout);
        }

        
// Static helper to which the above method
        
// can delegate to in order to get the default
        
// COM behavior.
        protected static extern int WaitHelper(IntPtr[] waitHandles,
                         
bool waitAll, int millisecondsTimeout);

        
// set SynchronizationContext on the current thread
        public static void SetSynchronizationContext(SynchronizationContext syncContext)
        {
            SetSynchronizationContext(syncContext,
              Thread.CurrentThread.ExecutionContext.SynchronizationContext);
        }

        
internal static SynchronizationContextSwitcher
          SetSynchronizationContext(SynchronizationContext syncContext,
          SynchronizationContext prevSyncContext)
        {
            
// get current execution context
            ExecutionContext ec = Thread.CurrentThread.ExecutionContext;
            
// create a switcher
            SynchronizationContextSwitcher scsw = new SynchronizationContextSwitcher();

            RuntimeHelpers.PrepareConstrainedRegions();
            
try
            {
                
// attach the switcher to the exec context
                scsw._ec = ec;
                
// save the current sync context using the passed in value
                scsw.savedSC = prevSyncContext;
                
// save the new sync context also
                scsw.currSC = syncContext;
                
// update the current sync context to the new context
                ec.SynchronizationContext = syncContext;
            }
            
catch
            {
                
// Any exception means we just restore the old SyncCtx
                scsw.UndoNoThrow(); //No exception will be thrown in this Undo()
                throw;
            }
            
// return switcher
            return scsw;
        }

        
// Get the current SynchronizationContext on the current thread
        public static SynchronizationContext Current
        {
            
get
            {
                ExecutionContext ec 
= Thread.CurrentThread.GetExecutionContextNoCreate();
                
if (ec != null)
                    
return ec.SynchronizationContext;
                
return null;
            }
        }

        
// helper to Clone this SynchronizationContext,
        public virtual SynchronizationContext CreateCopy()
        {
            
// the CLR dummy has an empty clone function - no member data
            return new SynchronizationContext();
        }

        
private static int InvokeWaitMethodHelper(SynchronizationContext syncContext,
            IntPtr[] waitHandles,
            
bool waitAll,
            
int millisecondsTimeout)
        {
            
return syncContext.Wait(waitHandles, waitAll, millisecondsTimeout);
        }
    }
}
注意Send和Post的部分:
public virtual void Send(SendOrPostCallback d, Object state)
{
    d(state);
}

public virtual void Post(SendOrPostCallback d, Object state)
{
    ThreadPool.QueueUserWorkItem(
new WaitCallback(d), state);
}
     Send就是简单在当前的线程上面去调用委托来实现,而Post是通过线程池来实现。
     这时候你也许会奇怪,为什么UI线程上,SynchronizationContext就发挥了不同的作用呢!其实在UI线程中使用的并不是SynchronizationContext这个类,而是WindowsFormsSynchronizationContext这个东东。
奇妙的SynchronizationContext
     它重写了Send和Post方法。至于它是如何重写实现的,这个我也不是很了解,也没有找到相关的文章,只是知道通过"消息泵"来实现的,但是细节就不清楚了,如果大家知道的话,可以告诉下我,我很想了解下!呵呵
     最后,我画了一副图,让我们更加清楚地了解SynchronizationContext在UI线程和一般线程之间的不同,
     奇妙的SynchronizationContext

     上一篇:线程之间的通讯---SynchronizationContext

相关文章: