Worker Threads in C#
作者:Alex Farber 翻译:小新0574
原文链接:http://www.codeproject.com/csharp/workerthread.asp
Introduction
The .NET framework provides a lot of ways to implement multithreading programs. I want to show how we can run a worker thread which makes syncronous calls to a user interface (for example, a thread that reads a long recordset and fills some control in the form).
.NET framework提供了很多方法来实现多线程程序。我想给大家演示怎么运行一个相对于用户界面实现同步调用的工作线程(比如说,一个读取一个很长的数据集来填充窗口中某个控件的线程)
To run thread I use:
为了运行线程,我使用:
Thread instance and main thread function
Two events used to stop thread. First event is set when main thread wants to stop worker thread; second event is set by worker thread when it really stops.
线程实例和主线程函数
两个用来结束线程的事件。在主线程想要停止工作线程的时候,第一个事件的状态被设置为终止;在工作线程实际结束的时候,第二个事件的状态被工作线程设置为终止。
.NET allows you to call System.Windows.Forms.Control functions only from the thread in which the control was created. To run them from another thread we need to use the Control.Invoke (synchronous call) or Control.BeginInvoke (asynchronous call) functions. For tasks like showing database records we need Invoke.
.NET只允许你在创建控件的线程调用System.Windows.Forms.Control 的函数。要在其它线程运行它们(函数)我们需要使用Control.Invoke (同步调用)或者Control.BeginInvoke (异步调用)函数。对于显示数据库记录这样的任务,我们需要Invoke。
To implement this we will use:
为了实现它我们要用:
A Delegate type for calling the form function. Delegate instance and function called using this delegate
The Invoke call from the worker thread.
一个用来调用窗口中的函数的委托类型。委托实例和使用这个委托调用的函数
在工作线程调用的Invoke 函数。
The next problem is to stop the worker thread correctly. The steps to do this are:
接下来的问题是怎么正确地终止工作线程。操作的步骤应该是:
Set the event "Stop Thread"
Wait for the event "Thread is stopped"
Wait for the event process messages using the Application.DoEvents function. This prevents deadlocks because the worker thread makes Invoke calls which are processed in the main thread.
设置"Stop Thread"事件的状态为终止
等待"Thread is stopped" 事件
等待使用Application.DoEvents 函数来让事件处理(当前在消息队列中的所有 Windows) 消息。这样可以预防死锁,因为工作线程调用Invoke 函数,而这个函数是在主线程里处理的。
The thread function checks every iteration whether the "Stop Thread" event has been set. If the event is set the function invokes clean-up operations, sets the event "Thread is stopped" and returns.
线程函数在每一次循环中检查是否"Stop Thread"事件被设置为终止了。如果该事件被设置为终止了,函数就调用一些清理操作,设置事件"Thread is stopped"为终止,同时返回。
Demo project has two classes: MainForm
and LongProcess
. The LongProcess.Run
function runs in a thread and fills the list box with some lines. The worker thread may finish naturally or may be stopped when user presses the "Stop Thread" button or closes the form.
演示工程有两个类:MainForm
和 LongProcess
。LongProcess.Run
函数在一个线程里运行,这个线程用来为列表框填充几行文字。工作线程可以自然结束或者可能在用户按了"Stop Thread"按钮或者关闭窗体时结束。
代码片断:
// MainForm.cs
namespace WorkerThread
{
// delegates used to call MainForm functions from worker thread
public delegate void DelegateAddString(String s);
public delegate void DelegateThreadFinished();
public class MainForm : System.Windows.Forms.Form
{
// ...
// worker thread
Thread m_WorkerThread;
// events used to stop worker thread
ManualResetEvent m_EventStopThread;
ManualResetEvent m_EventThreadStopped;
// Delegate instances used to call user interface functions
// from worker thread:
public DelegateAddString m_DelegateAddString;
public DelegateThreadFinished m_DelegateThreadFinished;
// ...
public MainForm()
{
InitializeComponent();
// initialize delegates
m_DelegateAddString = new DelegateAddString(this.AddString);
m_DelegateThreadFinished = new DelegateThreadFinished(this.ThreadFinished);
// initialize events
m_EventStopThread = new ManualResetEvent(false);
m_EventThreadStopped = new ManualResetEvent(false);
}
// ...
// Start thread button is pressed
private void btnStartThread_Click(object sender, System.EventArgs e)
{
// ...
// reset events
m_EventStopThread.Reset();
m_EventThreadStopped.Reset();
// create worker thread instance
m_WorkerThread = new Thread(new ThreadStart(this.WorkerThreadFunction));
m_WorkerThread.Name = "Worker Thread Sample"; // looks nice in Output window
m_WorkerThread.Start();
}
// Worker thread function.
// Called indirectly from btnStartThread_Click
private void WorkerThreadFunction()
{
LongProcess longProcess;
longProcess = new LongProcess(m_EventStopThread, m_EventThreadStopped, this);
longProcess.Run();
}
// Stop worker thread if it is running.
// Called when user presses Stop button or form is closed.
private void StopThread()
{
if ( m_WorkerThread != null && m_WorkerThread.IsAlive ) // thread is active
{
// set event "Stop"
m_EventStopThread.Set();
// wait when thread will stop or finish
while (m_WorkerThread.IsAlive)
{
// We cannot use here infinite wait because our thread
// makes syncronous calls to main form, this will cause deadlock.
// Instead of this we wait for event some appropriate time
// (and by the way give time to worker thread) and
// process events. These events may contain Invoke calls.
if ( WaitHandle.WaitAll(
(new ManualResetEvent[] {m_EventThreadStopped}),
100,
true) )
{
break;
}
Application.DoEvents();
}
}
}
// Add string to list box.
// Called from worker thread using delegate and Control.Invoke
private void AddString(String s)
{
listBox1.Items.Add(s);
}
// Set initial state of controls.
// Called from worker thread using delegate and Control.Invoke
private void ThreadFinished()
{
btnStartThread.Enabled = true;
btnStopThread.Enabled = false;
}
}
}
// LongProcess.cs
namespace WorkerThread
{
public class LongProcess
{
// ...
// Function runs in worker thread and emulates long process.
public void Run()
{
int i;
String s;
for (i = 1; i <= 10; i++)
{
// make step
s = "Step number " + i.ToString() + " executed";
Thread.Sleep(400);
// Make synchronous call to main form.
// MainForm.AddString function runs in main thread.
// (To make asynchronous call use BeginInvoke)
m_form.Invoke(m_form.m_DelegateAddString, new Object[] {s});
// check if thread is cancelled
if ( m_EventStop.WaitOne(0, true) )
{
// clean-up operations may be placed here
// ...
// inform main thread that this thread stopped
m_EventStopped.Set();
return;
}
}
// Make synchronous call to main form
// to inform it that thread finished
m_form.Invoke(m_form.m_DelegateThreadFinished, null);
}
}
}
有兴趣的朋友可以研究一下原页面可以下载的演示工程代码,很好的一个例子,本来我还想自己做一下解析,但是读了几遍作者的解释,觉得差不多了,不用我再费话了。
读国外的一些原代码,最强的感受到不在于写代码的技巧有多高,而在于注释做得太好了,几乎一些重要的语句都有注释说明作用,希望大家写的程序也都能有良好的注释。