奇妙的SynchronizationContext

来源:互联网 发布:淘宝海报尺寸是多少 编辑:程序博客网 时间:2024/04/27 16:24
上一篇中已经讲了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 is10
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 0
原创粉丝点击