代码改变世界

初探.NET多线程

2005-09-09 18:15 FantasySoft 阅读(...) 评论(...) 编辑 收藏
         好多天没有更新Blog了,一直都在忙着寻找如何将IronPython集成至GUI中的方法。一开始我把问题看得太简单了,因为我看了来自CodeProject的ShellControl - A console emulation control这篇文章之后,很幼稚地认为只需要把源代码中的cmd.exe换IronPythonConsole.exe 就可以大功告成了,然而事实证明我的想法是很愚蠢的。尽管IronPythonConsole与cmd同为可执行文件,但是它们与作为输入输出的RichTextBox之间的关系是不同的。对cmd而言,是一种顺序的处理方式:TextBox接受用户输入-> 用户按下Enter触发相应的EventHandler,在EventHandler中创建Process以运行cmd.exe -> 将结果返回至TextBox。在这个过程中,每一条指令的输入都会相应地创建新的Process,而这个Process的life cycle仅仅存在于用户按下Enter触发的事件响应函数中;反观IronPythonConsole的情况就不是这样的了,我们需要的是一个GUI Thread来响应用户的输入,同时需要一个Worker Thread调用IronPythonConsole来完成用户输入并给出相应的输出,很明显我需要进行多线程的开发了。面对着多线程,我真的很困窘,因为自己在这个方面的经验几乎为零。不过没有办法,为了FantasyPython,只能明知山有虎,偏向虎山行了。
        也许是自己天性愚钝吧,看了两天的文章,也还没有很好的solution,不过对于Multithreading倒是有了一点小小的认识,谨记于此。首先是使用多线程的重要性了。这点其实不必多言,大家都会知道把占用大量CPU周期的数据处理放到GUI Thread中,造成GUI无法响应用户的操作是一种十分糟糕的做法。为了实现更好的用户体验,就应该创建新的进程来处理持续时间较长的操作,通常我们会把负责非UI操作的进程称为Worker Thread(工作进程)。重要性说完了,让我们关注到更具体的多线程实现上来。
        在.NET中进行多线程开发,会有很多选择,其中最常见的就是使用Control.Invoke方法了。Invoke方法提供了一种在Worker Thread调用GUI Thread中delegate的途径。同时结合Control.InvokeRequired的属性,我们也可以轻易地判断某个线程是否为创建该Control的线程。为了对Invoke方法有更深入的认识,我仔细地阅读了CodeProject上《Worker Threads In C#》一文[1]。在文中有这样的代码:
m_form.Invoke(m_form.m_DelegateAddString, new Object[] {s});

这里给出了Invoke方法的一种使用方式,但是可惜的是,我并不能对其使用的必要性有充分的理解。因为我把这段代码改为m_form.AddString(s),并且将AddString方法的access modifier由private改为public,程序仍然是运行好好的。那么Invoke方法的意义在于什么呢?而MSDN给Invoke方法下的定义又是如此的简单:Executes the specified delegate on the thread that owns the control's underlying window handle. 真的是看不明白了,烦请各位朋友不吝赐教。
        问题虽然有,但是认识还得接着说。多线程开发很重要的一点就是线程间的通信问题,在CodeProject的这篇文章中,Worker Thread与GUI Thread是通过ManualResetEvent来实现的。我们从文中的代码:
// Worker Thread
if ( m_EventStop.WaitOne(0true) ) {
    m_EventStopped.Set();
    
return
;
}


// GUI Thread
if ( m_WorkerThread != null  &&  m_WorkerThread.IsAlive ) {
    m_EventStopThread.Set();
    
while (m_WorkerThread.IsAlive) 
{
        
if ( WaitHandle.WaitAll((new ManualResetEvent[] {m_EventThreadStopped}), 100,true) ) 
{
            
break
;
        }


        Application.DoEvents();
    }

}

  
可以看到Worker Thread与GUI Thread之间的通信过程:
        1、当Worker Thread处于活动状态,而GUI Thread通过设置m_EventStopThread的状态向Worker Thread发出Signal,并使自己进入循环等待状态,直至接收到由Worker Thread发出的Signal;
        2、Worker Thread在接收到该Signal之后采取相应的操作,例子中的操作就是通过设置m_EventStopped的状态向GUI Thread发出自己已经停止的Signal并且返回;
        3、GUI Thread接收到来自Worker Thread发出的Signal后,跳出循环;
        呼,终于说完了。由于自己刚刚开始接触C#的MultiThreading开发,疏漏一定不少。请各位路过的朋友不要错过扔砖头的机会哦,^_^

        [1] 小新0574为这篇不错的文章提供了译文,有兴趣的朋友可以参考一下中文版。