- 浏览: 10542 次
最新评论
BackgroundWork的内部实现
2010年06月07日
最近在学习多线程方面的东西,打算在自己的小程序中尝试使用。所以也看了不少文章。关于BackgroundWork,其实去年就用Reflector大概看过,但是没有太懂,呵呵,今天看相关文章正好又碰到,所以就仔细看看。
一 异步编程
在开始介绍BackgroundWork之前还是废话一下,说说异步编程。异步操作通常用于执行完成时间可能较长的任务,如打开大文件、连接远程计算机或查询数据库。异步操作在主应用程序线程以外的线程中执行。应用程序调用方法异步执行某个操作时,应用程序可在异步方法执行其任务时继续执行。
说到异步编程,就不能不说到多线程,他们之间的关系有时总是让人很迷惑。在学习的初期,你会觉得多线程和异步没有什么区别。我自己开一个新线程去执行一个任务和用异步执行一个任务感觉没什么区别。甚至分不清到底用什么。其实多线程是实现异步编程的一种方式,但不是实现异步编程的唯一方式。
如果学习过计算机体系结构就应该知道,CPU和外设之间通信有三种方式。比如请求打印机,当打印机没有准备好,这个时候就等待,直到打印机准备好,在执行操作,这个叫程序查询方式。但因为外设速度慢,浪费CPU时间;后来出现了硬件中断。在等待打印机的过程中,CPU分配给其他进程,当打印机准备好了,则发一个硬件中断,通知CPU打印。这样有效的提高了CPU的效率,但是频繁的中断和进程的切换,也降低了效率;于是就出现了DAM(Direct Memory Access),DMA最明显的一个特点是它不是用软件而是采用一个专门的控制器来控制内存与外设之间的数据交流,无须CPU介入,大大提高CPU的工作效率。在进行DMA数据传送之前,DMA控制器会向CPU申请总线控制权,CPU如果允许,则将控制权交出,因此,在数据交换时,总线控制权由DMA控制器掌握,在传输结束后,DMA控制器将总线控制权交还给CPU。
所以我们有多种实现异步的方式:利用多线程;IOCP 等,而IOCP就是使用了硬件的功能,虽然我们多起了一个线程,但是线程那I/O请求交给硬件驱动后就空闲了,回到线程池被其他调用。比如读取文件使用异步处理,需要把流修改为异步,并且调用Read的异步方法,这时候,线程把请求交给I/O就被回收了,流返回后在使用一个线程进行处理;如果只调用 Read异步方法,而流不支持异步,实际上是在内部用一个线程模拟了所谓的异步;最后就是流使支持异步,但使用同步的read方法,这个时候线程采用轮询的方法,等待操作完成。
二 .NET中的异步编程模型
在.NET 中有三种异步编程的方式,使用最多的是APM,我们可以看到,很多类都提供了BeginXXX,EndXXX这样的方法,就是异步方法。另外一种方法就是委托实现异步编程,委托提供了BeginInvoke、EndInvoke方法实现异步编程。他们有3种聚集的方式,分别是:等待完成、轮询和会回调。而最后一种异步编程模型就是基于事件驱动的,也就是我们今天要具体看的BackgroundWork控件,他就是一个基于事件驱动的复杂异步编程控件。
关于异步编程模型: http://msdn.microsoft.com/zh-cn/library/ms228969.aspx ,MSND上的介绍的很详细。是比较好的文章。
三 BackgroundWork
前面提到了,BackgroundWork是基于事件驱动的,所以使用起来要比APM方便使用过的应该知道,我们只需要只需要绑定3个事件,就能很轻松的完成复杂的异步操作,并且可以轻松获取执行的进度、取消执行等功能。下面就具体看看他的内部实现。为我们实现自己的异步功能也有所帮助。
BackgroundWork代码结构
代码中定义了DoWork、ProgressChanged、RunWorkerCompleted这三个事件,我们在使用时也只用把方法注册到时间上就行了。我们知道定义一个事件的同时,还会有一个引发该事件的protect virtual方法,在上面代码中就是OnDoWork、OnProgressChanged、OnRunWorkerCompleted这3个受保护的虚方法。他们的实现都很简单就是触发事件,下面是OnDoWork的实现,其他2个完全一样;
view plaincopy to clipboardprint?
protected virtual void OnDoWork(DoWorkEventArgs e)
{
DoWorkEventHandler handler = (DoWorkEventHandler) base.Events[doWorkKey];
if (handler != null)
{
handler(this, e);
}
}
protected virtual void OnDoWork(DoWorkEventArgs e)
{
DoWorkEventHandler handler = (DoWorkEventHandler) base.Events[doWorkKey];
if (handler != null)
{
handler(this, e);
}
}
接着看其他代码,里面还有RunWorkerAsync、ProgressReporter、ReportProgress、WorkerThreadStart等方法,看名字就明白,主要是启动和进度报告用。而私有字段中则定义了AsyncOperation对象和SendOrPostCallback委托,这2个是比较特殊也比较重要的,在后面在说。
客户端使用BackgroundWork
这里我并不打算介绍具体如何使用这个控件,而是我们在使用时的顺序:
1:给BackgroundWork的3个事件绑定处理的代码;(WORK方法、WORKING方法、WORKDOWN方法)
2:调用RunWorkerAsync 方法,开始执行异步操作
3:在DoWork事件绑定的方法中调用ReportProgress ,以便触发ProgressChanged事件,而执行ProgressChanged绑定的方法
4:执行完成后会触发RunWorkerCompleted事件,执行与之相绑定的方法。
注意到上面黑体的2个方法是我调用的方法,也是唯一提供的2个公有方法。整个过程也很好理解,我们要做的操作很少,而他的思路和APM中的回调方式比较相似,但是他相比起来更为灵活,因为一个事件可以挂在多个方法,而且在运行时可以实时返回进度。知道了他的过程,我们就来看看他具体是如何实现的。
内部实现
在看具体代码前,先看看类中定义的变量和委托,具体的意义已经写出来了,接下来就看看具体代码了
view plaincopy to clipboardprint?
// 工作线程启动委托
private delegate void WorkerThreadStartDelegate(object argument);
// 标识状态的变量
private bool isRunning; // 是否在执行
private bool canCancelWorker; // 能否取消执行
private bool cancellationPending; // 是否已请求取消后台操作
private bool workerReportsProgress; // 是否报告进度
private AsyncOperation asyncOperation; // 区分不同的异步任务
// 表示在消息即将被调度到同步上下文时要执行的回调方法
private readonly SendOrPostCallback operationCompleted;
private readonly SendOrPostCallback progressReporter;
// 工作线程启动委托的方法
private readonly WorkerThreadStartDelegate threadStart;
// 触发事件是使用的对象
private static readonly object doWorkKey = new object();
private static readonly object progressChangedKey = new object();
private static readonly object runWorkerCompletedKey = new object();
// 工作线程启动委托
private delegate void WorkerThreadStartDelegate(object argument);
// 标识状态的变量
private bool isRunning; // 是否在执行
private bool canCancelWorker; // 能否取消执行
private bool cancellationPending; // 是否已请求取消后台操作
private bool workerReportsProgress; // 是否报告进度
private AsyncOperation asyncOperation; // 区分不同的异步任务
// 表示在消息即将被调度到同步上下文时要执行的回调方法
private readonly SendOrPostCallback operationCompleted;
private readonly SendOrPostCallback progressReporter;
// 工作线程启动委托的方法
private readonly WorkerThreadStartDelegate threadStart;
// 触发事件是使用的对象
private static readonly object doWorkKey = new object();
private static readonly object progressChangedKey = new object();
private static readonly object runWorkerCompletedKey = new object();
构造方法:构造方法里面很简单,初始化了私有变量中的3个委托对象;他们都是3个私有的方法
view plaincopy to clipboardprint?
public BackgroundWorker()
{
this.threadStart = new WorkerThreadStartDelegate(this.WorkerThreadStart);
this.operationCompleted = new SendOrPostCallback(this.AsyncOperationCompleted);
this.progressReporter = new SendOrPostCallback(this.ProgressReporter);
}
public BackgroundWorker()
{
this.threadStart = new WorkerThreadStartDelegate(this.WorkerThreadStart);
this.operationCompleted = new SendOrPostCallback(this.AsyncOperationCompleted);
this.progressReporter = new SendOrPostCallback(this.ProgressReporter);
}
我们先来看看ProgressReporter 和AsyncOperationCompleted 这2个私有方法的代码:是不是也很简单,就是调用触发了事件方法。
view plaincopy to clipboardprint?
private void AsyncOperationCompleted(object arg)
{
this.isRunning = false;
this.cancellationPending = false;
this.OnRunWorkerCompleted((RunWorkerCompletedEventArgs) arg);
}
private void ProgressReporter(object arg)
{
this.OnProgressChanged((ProgressChangedEventArgs) arg);
}
private void AsyncOperationCompleted(object arg)
{
this.isRunning = false;
this.cancellationPending = false;
this.OnRunWorkerCompleted((RunWorkerCompletedEventArgs) arg);
}
private void ProgressReporter(object arg)
{
this.OnProgressChanged((ProgressChangedEventArgs) arg);
}
看完了初始化,以及委托相关的方法,在客户端我们的程序就要启动了,这个时候我们调用RunWorkerAsync 方法
view plaincopy to clipboardprint?
public void RunWorkerAsync(object argument)
{
if (this.isRunning)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning"));
}
this.isRunning = true;
this.cancellationPending = false;
this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.threadStart.BeginInvoke(argument, null, null);
}
public void RunWorkerAsync(object argument)
{
if (this.isRunning)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning"));
}
this.isRunning = true;
this.cancellationPending = false;
this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.threadStart.BeginInvoke(argument, null, null);
}
代码中首先判断是否在执行,如果已经执行则抛出一个异常,也就是说我们的BackgroundWork不支持并发处理,所以我们在自己代码中,调用RunWorkerAsync 方法之前,应该判断IsBusy 属性(它返回的就是isRunning ), 而不是让系统抛出异常然后去捕获。接下来就是设置字段状态。然后实例化了asyncOperation 对象,这个对象后面在介绍。最主要的就是最后一句,threadStart.BeginInvoke 这里是我们前面提到的 利用委托实现异步,这个也是BackgroundWork异步的内部实现。委托的BeginInvoke是开启一个新的线程执行,这里执行的委托就是在构造函数中设置的WorkerThreadStart 方法
view plaincopy to clipboardprint?
private void WorkerThreadStart(object argument)
{
object result = null;
Exception error = null;
bool cancelled = false;
try
{
DoWorkEventArgs e = new DoWorkEventArgs(argument);
this.OnDoWork(e);
if (e.Cancel)
{
cancelled = true;
}
else
{
result = e.Result;
}
}
catch (Exception exception2)
{
error = exception2;
}
RunWorkerCompletedEventArgs arg = new RunWorkerCompletedEventArgs(result, error, cancelled);
this.asyncOperation.PostOperationCompleted(this.operationCompleted, arg);
}
private void WorkerThreadStart(object argument)
{
object result = null;
Exception error = null;
bool cancelled = false;
try
{
DoWorkEventArgs e = new DoWorkEventArgs(argument);
this.OnDoWork(e);
if (e.Cancel)
{
cancelled = true;
}
else
{
result = e.Result;
}
}
catch (Exception exception2)
{
error = exception2;
}
RunWorkerCompletedEventArgs arg = new RunWorkerCompletedEventArgs(result, error, cancelled);
this.asyncOperation.PostOperationCompleted(this.operationCompleted, arg);
}
代码中的this.OnDoWork(e); 触发了DoWork事件绑定的方法,也就是我们需要异步操作的方法。因为这个方法是在新的线程中执行,而不是在UI线程,这也就是为什么我们不能在自己的WORK方法中操作界面控件。(为什么不能?因为VS2005开始强制不允许非控件创建线程修改控件。当然这还是有办法的,具体可以参见:http://www.cnblogs.com/yizhu2000/archive/2008/01/03/1011958.html#wm1 这里介绍了在非UI线程修改控件的办法,不过当你看完本文,你会发现BackgroundWork用了一种不可思议的方式来实现)。
这个时候我们的WORK方法开始执行了,而UI线程可以执行其他操作,比如重绘,界面也不在会假死。因为我们在WORK方法中调用了ReportProgress 方法来触发ProgressChanged事件,以便我们的WORKING方法能够被执行。下面就看看ReportProgress 方法
view plaincopy to clipboardprint?
public void ReportProgress(int percentProgress, object userState)
{
if (!this.WorkerReportsProgress)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntReportProgress"));
}
ProgressChangedEventArgs arg = new ProgressChangedEventArgs(percentProgress, userState);
if (this.asyncOperation != null)
{
this.asyncOperation.Post(this.progressReporter, arg);
}
else
{
this.progressReporter(arg);
}
}
public void ReportProgress(int percentProgress, object userState)
{
if (!this.WorkerReportsProgress)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntReportProgress"));
}
ProgressChangedEventArgs arg = new ProgressChangedEventArgs(percentProgress, userState);
if (this.asyncOperation != null)
{
this.asyncOperation.Post(this.progressReporter, arg);
}
else
{
this.progressReporter(arg);
}
}
首先检查是否能够报告工作进度,如果不行,会跑出异常,所以如果要使用他,我们要把控件的WorkerReportsProgress属性设置为True。接下来的代码有些难以理解。主要是asyncOperation对象。MSDN给出的定义是:根据基于事件的异步模式概述实现类时,可能需要跟踪在类的实例上调用的每个异步操作的生存期。AsyncOperation 类提供了多种方法来跟踪和报告异步任务的进度。若要将进度和中间结果报告给客户端,请从异步辅助代码调用 Post。若要指示异步任务已经完成,或要取消挂起的异步任务,请调用 PostOperationCompleted。
但是在执行之前,先判断了asyncOperation是否为null,如果不为null,调用psot方法;如果为空怎直接执行progressReporter委托。 但是我们可以发现,这样的话有问题,因为执行委托方法是在非UI线程,如果WORKING事件中有操作UI控件就会抛出异常。而post方法很特殊,他是把当前方法发送到UI线程上执行。但是这里好像不会出现asyncOperation为空的情况,不过不为空应该抛出异常或什么处理也不做比较合适。
这里的post方法的一个参数是SendOrPostCallback 委托,另一个就是要传递的对象,而参数progressReporter我们前面已经知道了,她是指向了触发ProgressReporter方法,来触发ProgressChanged事件。所以我们的WORKING实际上是通过:WORK方法-->ReportProgress方法-->(post方法)-->progressReporter委托 -->ProgressReporter方法-->OnProgressChanged方法-->ProgressChanged事件-->WORKING方法
我们在WORK中每调用一次ReportProgress方法,就会执行一次我们自定义的WORKING方法,或许你会问了,那为什么不直接在WORK中调用WORKING,虽然这样是可以,但是还是那个问题,非UI线程不能修改UI控件,所以中间转了这么一道,progressReporter委托 -->ProgressReporter 这一步就是为了利用了post方法。而第2个参数在整个过程中起到了传递数据的作用。
当WORK方法执行完成后,获取result对象,而最后调用了asyncOperation 对象的PostOperationCompleted 方法。他也的参数也是一个SendOrPostCallback 委托,指向了AsyncOperationCompleted方法,方法代码如下:
view plaincopy to clipboardprint?
private void AsyncOperationCompleted(object arg)
{
this.isRunning = false;
this.cancellationPending = false;
this.OnRunWorkerCompleted((RunWorkerCompletedEventArgs) arg);
}
private void AsyncOperationCompleted(object arg)
{
this.isRunning = false;
this.cancellationPending = false;
this.OnRunWorkerCompleted((RunWorkerCompletedEventArgs) arg);
}
他的过程是:WORK方法执行完-->PostOperationCompleted 方法-->operationCompleted委托-- >AsyncOperationCompleted方法-->OnRunWorkerCompleted方法-->RunWorkerCompleted事件-->WORKDOWN方法 可以发现他和前面的过程是一样的,这里离就不过多解释了。
取消执行
要取消正在执行的任务时,可以调用CancelAsync方法:
view plaincopy to clipboardprint?
public void CancelAsync()
{
if (!this.WorkerSupportsCancellation)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntSupportCancellation"));
}
this.cancellationPending = true;
}
public void CancelAsync()
{
if (!this.WorkerSupportsCancellation)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntSupportCancellation"));
}
this.cancellationPending = true;
}
代码也是会去检查是否支持取消执行,所以如果我们允许取消,就要在使用前把WorkerSupportsCancellation设置为true。否则会抛出异常。
AsyncOperation对象
看完了BackgroundWorker的代码,发现其实挺简单的,代码也很少,他的核心思想就是:基于事件的异步模型。我们只需要关联3个事件就能完成一部操作。因为工作方法在非UI线程上执行,为了操作UI控件,利用了post等方法,以一种不可思议的形式,把执行的方法从当前线程发送到了UI线程,而这也是 BackgroundWorker的核心,而AsyncOperation 对象就是核心。所以我们可以利用他来建立我们自己的异步控件。
上面是AsyncOperation类, 其中Post和PostOperationCompleted就是我们用到的方法,他们有相同的参数
view plaincopy to clipboardprint?
public void Post(SendOrPostCallback d, object arg)
{
this.VerifyNotCompleted();
this.VerifyDelegateNotNull(d);
this.syncContext.Post(d, arg);
}
public void PostOperationCompleted(SendOrPostCallback d, object arg)
{
this.Post(d, arg);
this.OperationCompletedCore();
}
public void Post(SendOrPostCallback d, object arg)
{
this.VerifyNotCompleted();
this.VerifyDelegateNotNull(d);
this.syncContext.Post(d, arg);
}
public void PostOperationCompleted(SendOrPostCallback d, object arg)
{
this.Post(d, arg);
this.OperationCompletedCore();
}
发现,PostOperationCompleted和post一样,只是最后执行了一个操作完成的方法。在Post方法中,前2个方法是验证异步调用是否已经执行完以及SendOrPostCallback委托是否绑定了方法(如果没有,我们前台方法无法执行,就失去了意义)。
view plaincopy to clipboardprint?
public virtual void Post(SendOrPostCallback d, object state)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state);
}
public virtual void Post(SendOrPostCallback d, object state)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state);
}
在看看syncContext.Post(d , arg )方法的实现,她是调用了SynchronizationContext对象的post方法,SynchronizationContext类MSDN的解释是:此类实现的同步模型的目的是使公共语言运行库内部的异步/同步操作能够针对不同的异步模型采取正确的行为。此模型还简化了托管应用程序为在不同的同步环境下正常工作而必须遵循的一些要求。
也正式他是的我们非UI线程上的方法,可以采用正确的行为,那就是使用UI线程操作。而Post方法是将异步消息调度到一个同步上下文。 按道理,是发送到UI线程上,但是在代码中看到是用的是使用了线程池中的一个线程。这里的行为以及POST到底是如何工作的就很让人费解了。但是注意,他是一个虚方法,可能被重写,那么我们看看syncContext 是怎么创建的。首先syncContext 是一个asyncOperation对象内部维护的一个字段。那么我们就要看看asyncOperation对象创建的情况,还记得前面this.asyncOperation = AsyncOperationManager.CreateOperation(null); 的代码,他是一个AsyncOperationManager对象来创建的。我们看看这个类
view plaincopy to clipboardprint?
[HostProtection(SecurityAction.LinkDemand, SharedState=true)]
// AsyncOperationManager类
public static class AsyncOperationManager
{
// Methods
public static AsyncOperation CreateOperation(object userSuppliedState)
{
return AsyncOperation.CreateOperation(userSuppliedState, SynchronizationContext);
}
// Properties
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static SynchronizationContext SynchronizationContext
{
get
{
if (SynchronizationContext.Current == null)
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
return SynchronizationContext.Current;
}
[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
set
{
SynchronizationContext.SetSynchronizationContext(value);
}
}
}
// AsyncOperation类的创建
internal static AsyncOperation CreateOperation(object userSuppliedState, SynchronizationContext syncContext)
{
return new AsyncOperation(userSuppliedState, syncContext);
}
[HostProtection(SecurityAction.LinkDemand, SharedState=true)]
// AsyncOperationManager类
public static class AsyncOperationManager
{
// Methods
public static AsyncOperation CreateOperation(object userSuppliedState)
{
return AsyncOperation.CreateOperation(userSuppliedState, SynchronizationContext);
}
// Properties
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static SynchronizationContext SynchronizationContext
{
get
{
if (SynchronizationContext.Current == null)
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
return SynchronizationContext.Current;
}
[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
set
{
SynchronizationContext.SetSynchronizationContext(value);
}
}
}
// AsyncOperation类的创建
internal static AsyncOperation CreateOperation(object userSuppliedState, SynchronizationContext syncContext)
{
return new AsyncOperation(userSuppliedState, syncContext);
}
这是他的全部代码,他实际还是利用AsyncOperation对象自己的CreateOperation方法,其中第2个参数就是我们这里的上下文对象,而它正是AsyncOperationManager对象的SynchronizationContext 属性。我们也就是要看看这个属性的创建过程。我们看看上面get方法中的代码
view plaincopy to clipboardprint?
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.ControlPolicy | SecurityPermissionFlag.ControlEvidence)]
public static void SetSynchronizationContext(SynchronizationContext syncContext)
{
SetSynchronizationContext(syncContext, Thread.CurrentThread.ExecutionContext.SynchronizationContext);
}
// 具体实现
internal static SynchronizationContextSwitcher SetSynchronizationContext(SynchronizationContext syncContext, SynchronizationContext prevSyncContext)
{
ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext;
SynchronizationContextSwitcher switcher = new SynchronizationContextSwitcher();
RuntimeHelpers.PrepareConstrainedRegions();
try
{
switcher._ec = executionContext;
switcher.savedSC = prevSyncContext;
switcher.currSC = syncContext;
executionContext.SynchronizationContext = syncContext;
}
catch
{
switcher.UndoNoThrow();
throw;
}
return switcher;
}
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.ControlPolicy | SecurityPermissionFlag.ControlEvidence)]
public static void SetSynchronizationContext(SynchronizationContext syncContext)
{
SetSynchronizationContext(syncContext, Thread.CurrentThread.ExecutionContext.SynchronizationContext);
}
// 具体实现
internal static SynchronizationContextSwitcher SetSynchronizationContext(SynchronizationContext syncContext, SynchronizationContext prevSyncContext)
{
ExecutionContext executionContext = Thread.CurrentThread.ExecutionContext;
SynchronizationContextSwitcher switcher = new SynchronizationContextSwitcher();
RuntimeHelpers.PrepareConstrainedRegions();
try
{
switcher._ec = executionContext;
switcher.savedSC = prevSyncContext;
switcher.currSC = syncContext;
executionContext.SynchronizationContext = syncContext;
}
catch
{
switcher.UndoNoThrow();
throw;
}
return switcher;
}
调用了一个 SetSynchronizationContext,看名字就明白了,是设置同步上下文的方法。 ExecutionContext类为与执行的逻辑线程相关的所有信息提供单个容器。这包括安全上下文、调用上下文、同步上下文、本地化上下文和事务上下文。在代码中设置了同步上下文,而get方法中最后返回了:return SynchronizationContext.Current;看看他的实现:
view plaincopy to clipboardprint?
public static SynchronizationContext Current
{
get
{
ExecutionContext executionContextNoCreate = Thread.CurrentThread.GetExecutionContextNoCreate();
if (executionContextNoCreate != null)
{
return executionContextNoCreate.SynchronizationContext;
}
return null;
}
}
public static SynchronizationContext Current
{
get
{
ExecutionContext executionContextNoCreate = Thread.CurrentThread.GetExecutionContextNoCreate();
if (executionContextNoCreate != null)
{
return executionContextNoCreate.SynchronizationContext;
}
return null;
}
}
倒是挺绕啊,返回了ExecutionContext对象的同步上下文属性,而这个我们在前面设置了。
而WindowsFormsSynchronizationContext 类型 为提供 Windows 窗体应用程序模型的同步上下文。他是SynchronizationContext的子类,在回到开始的asyncOperatio 对象创建,是在UI 线程中执行的。下面是她重写的Post方法:
view plaincopy to clipboardprint?
public override void Post(SendOrPostCallback d, object state)
{
if (this.controlToSendTo != null)
{
this.controlToSendTo.BeginInvoke(d, new object[] { state });
}
}
public override void Post(SendOrPostCallback d, object state)
{
if (this.controlToSendTo != null)
{
this.controlToSendTo.BeginInvoke(d, new object[] { state });
}
}
她调用了一个controlToSendTo 变量的BeginInvoke方法,在去看看这个变量 private Control controlToSendTo ;
哈哈,怎么样,前面还认为不可思议的东西,原来也还是回到了老路上,利用了控件的BeginInvoke方法来更新界面,而这恰恰是非UI线程更新UI控件的一种方法。
具体参见:http://www.cnblogs.com/worldreason/archive/2008/06/09/1216127.html
而对于SynchronizationContext详细的解释参见:http://www.codeproject.com/KB/threads/SynchronizationContext.aspx
其实关于多线程、异步调用、beginInvoke等都涉及到WINDWOS的消息机制和线程通信。想要了解清楚,还是要对WINDOWS消息机制和一场常用的API方法有所了解。哈,我了解的还很好,毕竟没搞过C++方面的,还得多多学习~~,今天就到这吧。不早了。还要试试LoadRunner。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cc_net/archive/2009/11/11/4796109.aspx
发表评论
-
文件操作小结
2012-01-20 00:39 709文件操作小结 2010年08 ... -
c语言的文件处理
2012-01-20 00:38 744c语言的文件处理 2010年06月03日 本章要点: ... -
Linux下文件I/O操作详解
2012-01-20 00:38 535Linux下文件I/O操作详解 2010年12月26日 ... -
fread函数和fwrite函数
2012-01-20 00:38 581fread函数和fwrite函数 2010年06月28日 ... -
虚拟机随谈:解释器,树遍历解释器,基于栈与基于寄存器,大杂烩
2012-01-20 00:38 833虚拟机随谈:解释器,树 ... -
File_operations结构体
2012-01-17 00:57 631File_operations结构体 2010 ... -
ORACLE中带参数、REF游标及动态SQL实例
2012-01-17 00:56 1204ORACLE中带参数、REF游标及动态SQL实例 2011年 ... -
oracle 载
2012-01-17 00:56 645oracle 载 2011年08月03日 ... -
linux字符设备驱动程序的file_operations成员解释
2012-01-17 00:56 1109linux字符设备驱动程序 ... -
linux C库函数(三)
2012-01-15 19:40 490linux C库函数(三) 2010年0 ... -
meego开发的学习路线
2012-01-15 19:40 378meego开发的学习路线 2011年06月26日 不同版 ... -
memmem 函数
2012-01-15 19:40 700memmem 函数 2011年06月02日 软件研发的面 ... -
文件读写函数的使用方法
2012-01-15 19:40 606文件读写函数的使用方法 2010年04月11日 原文地址 ... -
宝供实习
2012-01-15 19:40 459宝供实习 2010年04月04日 响宝供实习已经有 ...
相关推荐
后台backgroundwork使用实例,包含了如何直接使用,同时包含了进度条代码设计,可以以进度条形式显示任务执行的进度
在WinForm程序中,当程序正在执行耗时的任务时,Form会出现用户界面停止响应的情况;...这时候可以新建一个线程,把耗时的任务放到线程中,也可以通过BackgroundWork来实现,并且BackgroundWork理解起来比较容易。
后台控件,可开启后台进程,进行一些耗时操作,或者做流程控制,源码中包括窗体控件中的内容,转化为xml文件;xml文件中的内容读取出来,显示在窗体控件上
C# 的Backgroundwork 用法实例,多线程调用窗体资源,ReportProgress()参数用法。
学习如何使用backgroundwork 如何使用委托 vs2008编译通过
C#如何使用BackgroundWork后台辅助线程控件 不可多得的好文档
自动化变形监测之异步线程后台backgroundwork使用测试,采集、平差、管理三个线程运行。backgroundWorker1_DoWork backgroundWorker1_RunWorkerCompleted
使用,BackgroundWorker,组件,进行异步操作,编程 使用,BackgroundWorker,组件,进行异步操作,编程 使用,BackgroundWorker,组件,进行异步操作,编程 使用,BackgroundWorker,组件,进行异步操作,编程 ...
1、简单多线程 BackgroundWorker 控制(开始、暂停、继续、停止) 2、刷新进度条提示 3、刷新文本框提示
C# backgroundWorker控件使用C# backgroundWorker控件使用C# backgroundWorker控件使用C# backgroundWorker控件使用C# backgroundWorker控件使用C# backgroundWorker控件使用
c# BackGroundWorker 多线程操作的小例子,例子写的非常好
下载文件是常见任务,通常情况下,最好以单独的线程来运行这项可能很耗时的操作。使用 BackgroundWorker 组件可以用非常少的代码完成此任务
自己做的一个下载软件,可以监视剪贴板 类型右键使用迅雷下载功能 使用backgroundwork控件下载
本程序由VB.NET编写,模拟雷达扫描的画面。 采用backgroundwork组件,不会造成扫描时程序卡顿。 并写了一个简单的设置点并绘画在扫描图上的事件,可以用它来模拟发现设置距离和角度的物件并表示在扫描图上。
使用backgroundwork创建的一个有关进度条的小程序,包含代码,
C# BackgroundWorker TEST 简单测试程序,对backgroundwork空间进行了简单的测试