奇妙的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线程和一般线程之间的不同,
    

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值