奇妙的SynchronizationContext

 上一篇中已经讲了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原代码 ):


namespace  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这个东东。

     它重写了Send和Post方法。至于它是如何重写实现的,这个我也不是很了解,也没有找到相关的文章,只是知道通过"消息泵"来实现的,但是细节就不清楚了,如果大家知道的话,可以告诉下我,我很想了解下!呵呵
     最后,我画了一副图,让我们更加清楚地了解SynchronizationContext在UI线程和一般线程之间的不同,
     奇妙的SynchronizationContext_第1张图片

你可能感兴趣的:(奇妙的SynchronizationContext)