System.ComponentModel.AsyncOperation类 - 这个类很特别

 

昨天在尝试使用System.ComponentModel.BackgroundWorker时,发现这个类的行为和我预料的大不一样,可以说是惊喜。原来以为这个类只是一个线程的简单包装,用多线程模拟了异步调用而已;但是看下面的这段代码:

Thread.CurrentThread.Name = "Main Thread";
backgroundWorker1.RunWorkerAsync();

...

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    int i = 0;
    while (i++ < 100)
    {
        backgroundWorker1.ReportProgress(i);
        Thread.Sleep(50);
    }
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.Text = e.ProgressPercentage + "% - " + Thread.CurrentThread.Name;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    this.Text = "DONE - " + Thread.CurrentThread.Name;
}

毫无疑问,_DoWork方法是运行在另一个不同线程之上的(很容易验证这一点,这也符合BackgroundWorker的设计),而这个方法又调用了backgroundWorker1.ReportProgress方法,触发了ProgressChanged事件。在通常的异步实现,_ProgressChanged方法应该运行于事件触发者相同的线程中;但在这里,它运行于主线程(名为Main Thread的线程)。_RunWorkerCompleted方法也是一样。

在我看来,这个行为非常特别,实际上它也非常有用。这样_ProgressChanged方法体中操作UI控件的代码都无需使用Control.Invoke包装了,让程序的编写大为简化。而我真正感兴趣的是这个类究竟是怎么实现的,我用Reflector打开它的源码之后,原来关键在于它用到了一个名为AsyncOperation的类(System.ComponentModel.AsyncOperation)。

AsyncOperation类有个Post方法,可以用来把一个委托(作为方法指针/列表)提交给另一个线程执行。继续反编译下去,又查到了System.Threading.SynchronizationContext类。不过具体怎么实现是无从得知了,因为追踪到最后,停在了一个[MethodImpl(MethodImplOptions.InternalCall)]的方法,它由CLR本身实现。(我个人猜测,其中很可能利用了Windows API:Get/SetThreadContext,和结构体CONTEXT,改掉了线程上下文。)

退一步说,它怎么实现的并不是那么重要,重要的是我们可以用这个AsyncOperation类实现自己的BackgroundWorker。这里是我写的和上面代码基本等价的实现:

AsyncOperation asyncOperation;
SendOrPostCallback progressReporter;
Thread workerThread;

public MainForm()
{
    InitializeComponent();

    asyncOperation = AsyncOperationManager.CreateOperation(null);
    progressReporter = new SendOrPostCallback(ReportProgress);
    workerThread = new Thread(new ThreadStart(WorkOnWorkerThread));
}

private void MainForm_Load(object sender, EventArgs e)
{
    Thread.CurrentThread.Name = "Main Thread";
    workerThread.Name = "Worker Thread";
    workerThread.IsBackground = true;
    workerThread.Start();
}

void ReportProgress(object obj)
{
    this.Text = obj.ToString() + "% - " + Thread.CurrentThread.Name;
}

void WorkOnWorkerThread()
{
    int i = 0;
    while (i++ < 100)
    {
        asyncOperation.Post(progressReporter, i);
        Thread.Sleep(50);
    }
}

你可能感兴趣的:(.NET,C#)