您的位置:首页 > 产品设计 > UI/UE

线程之间的通讯- 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原代码):

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
综述

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: