线程之间的通讯- UI 通讯使用SynchronizationContext--(2)
2013-06-05 13:48
1791 查看
上一篇中已经讲了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原代码):
注意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线程和一般线程之间的不同,
完整的WindowsFormsSynchronizationContext代码
原文地址: /article/5087960.html
继上篇中的问题"在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线程和一般线程之间的不同,
完整的WindowsFormsSynchronizationContext代码
public sealed class WindowsFormsSynchronizationContext : SynchronizationContext, IDisposable { // Fields private Control controlToSendTo; private WeakReference destinationThreadRef; [ThreadStatic] private static bool dontAutoInstall; [ThreadStatic] private static bool inSyncContextInstallation; [ThreadStatic] private static SynchronizationContext previousSyncContext; // Methods public WindowsFormsSynchronizationContext() { this.DestinationThread = Thread.CurrentThread; Application.ThreadContext context = Application.ThreadContext.FromCurrent(); if (context != null) { this.controlToSendTo = context.MarshalingControl; } } private WindowsFormsSynchronizationContext(Control marshalingControl, Thread destinationThread) { this.controlToSendTo = marshalingControl; this.DestinationThread = destinationThread; } public override SynchronizationContext CreateCopy() { return new WindowsFormsSynchronizationContext(this.controlToSendTo, this.DestinationThread); } public void Dispose() { if (this.controlToSendTo != null) { if (!this.controlToSendTo.IsDisposed) { this.controlToSendTo.Dispose(); } this.controlToSendTo = null; } } internal static void InstallIfNeeded() { if (AutoInstall && !inSyncContextInstallation) { if (SynchronizationContext.Current == null) { previousSyncContext = null; } if (previousSyncContext == null) { inSyncContextInstallation = true; try { SynchronizationContext synchronizationContext = AsyncOperationManager.SynchronizationContext; if ((synchronizationContext == null) || (synchronizationContext.GetType() == typeof(SynchronizationContext))) { previousSyncContext = synchronizationContext; new PermissionSet(PermissionState.Unrestricted).Assert(); try { AsyncOperationManager.SynchronizationContext = new WindowsFormsSynchronizationContext(); } finally { CodeAccessPermission.RevertAssert(); } } } finally { inSyncContextInstallation = false; } } } } public override void Post(SendOrPostCallback d, object state) { if (this.controlToSendTo != null) { this.controlToSendTo.BeginInvoke(d, new object[] { state }); } } public override void Send(SendOrPostCallback d, object state) { Thread destinationThread = this.DestinationThread; if ((destinationThread == null) || !destinationThread.IsAlive) { throw new InvalidAsynchronousStateException(SR.GetString("ThreadNoLongerValid")); } if (this.controlToSendTo != null) { this.controlToSendTo.Invoke(d, new object[] { state }); } } public static void Uninstall() { Uninstall(true); } internal static void Uninstall(bool turnOffAutoInstall) { if (AutoInstall && (AsyncOperationManager.SynchronizationContext is WindowsFormsSynchronizationContext)) { try { new PermissionSet(PermissionState.Unrestricted).Assert(); if (previousSyncContext == null) { AsyncOperationManager.SynchronizationContext = new SynchronizationContext(); } else { AsyncOperationManager.SynchronizationContext = previousSyncContext; } } finally { previousSyncContext = null; CodeAccessPermission.RevertAssert(); } } if (turnOffAutoInstall) { AutoInstall = false; } } // Properties [EditorBrowsable(EditorBrowsableState.Advanced)] public static bool AutoInstall { get { return !dontAutoInstall; } set { dontAutoInstall = !value; } } private Thread DestinationThread { get { if ((this.destinationThreadRef != null) && this.destinationThreadRef.IsAlive) { return (this.destinationThreadRef.Target as Thread); } return null; } set { if (value != null) { this.destinationThreadRef = new WeakReference(value); } } } }
原文地址: /article/5087960.html
MSDN:SynchronizationContext
综述
相关文章推荐
- 线程之间的通讯- UI 通讯使用SynchronizationContext--(1)
- 使用HandlerThread创建线程并与UI线程通讯
- 例2:使用管道流完成线程之间的通讯。
- android 使用Handler UI线程和子线程通讯 更新UI
- android使用ExecutorService来处理子线程与UI线程的通讯问题
- C#学习之UI线程和线程池线程之间通讯之TaskScheduler.FromCurrentSynchronizationContext()
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- 线程之间共享数据(两个线程之间使用同一份数据),线程锁要统一,不统一的话会出现线程安全问题
- java线程基础巩固---多Produce多Consume之间的通讯导致出现程序假死的原因分析
- 使用Handler在主线程和子线程之间传值
- Android Jni中使用线程及回调更新UI
- 线程之间的通讯---SynchronizationContext
- 多线程 生产者消费者模式 线程之间的通讯问题
- 线程开发之多线程之间的通讯实现
- Message及其使用,线程间通讯机制
- 使用GCD处理几个线程之间的依赖关系。
- JavaSE第一百零四讲:哲学家就餐问题、死锁与使用wait及notify方法实现线程之间的相互通信
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- 使用泛型SwingWorker与EDT事件分发线程保持通讯