多线程开发
前不久写了一个项目,其中需要处理大量的数据,这些数据都是记录型的,对这些数据进行处理是一个非常耗时的事情,.net在处理这些使用的过程中,就好像死机一样一动不动。这样给用户的感觉非常的不好。例如如下的例子,我们在一个窗体上放置一个ProgressBar,然后对ProgressBar进行循环添加,例如:
2 {
3 int nStart = Convert.ToInt32(txtStart.Text);
4 int nEnd = Convert.ToInt32(txtEnd.Text);
5
6 prgProcess.Minimum = nStart;
7 prgProcess.Maximum = nEnd;
8 for (int nValue = nStart; nValue <= nEnd; nValue++)
9 {
10 lblStatus.Text = "Processing item: " + Convert.ToString(nValue);
11 prgProcess.Value = nValue;
12 } }
这段代码运行时正常的,但是对于数据量很大的时候,比如txtEnd.Text设置成1000000,这个时候界面上的进度条也许就是个固定值,一动不动,而且lblStatus.Text的现实也是为空,这是很正常的。程序就如同死机一样。当然,我们可以在循环中添加Application.DoEvents();使系统在空闲的时间处理其他的事情,这样系统的效果会好些。例如:
2 {
3 int nStart = Convert.ToInt32(txtStart.Text);
4 int nEnd = Convert.ToInt32(txtEnd.Text);
5
6 prgProcess.Minimum = nStart;
7 prgProcess.Maximum = nEnd;
8 for (int nValue = nStart; nValue <= nEnd; nValue++)
9 {
10 lblStatus.Text = "Processing item: " + Convert.ToString(nValue);
11 prgProcess.Value = nValue;
12
13 Application.DoEvents();
14 }
15 }
但这些不是本文的重点,本文的重点是使用多线程的的方式去处理这些内容,我们将在此窗体中添加Tread去处理这些复杂的操作。这样的效果会比Application.DoEvents();更好。
还是在原来的窗体上,我们使用线程的方式去处理。
2 DelegateType TheDelegate;
3
4 int StartFrom, EndTo;
5
6 private void btnStartThreaded_Click(object sender, EventArgs e)
7 {
8 TheDelegate = MessageHandler;
9
10 StartFrom = Convert.ToInt32(txtStart.Text);
11 EndTo = Convert.ToInt32(txtEnd.Text);
12
13 prgProcess.Minimum = StartFrom;
14 prgProcess.Maximum = EndTo;
15
16 btnStartThreaded.Enabled = false;
17
18 Thread MyThread = new Thread(ProcessRoutine);
19 MyThread.Start();
20 }
21
22 void MessageHandler(int nProgress)
23 {
24 lblStatus.Text = "Processing item: " + Convert.ToString(nProgress);
25 prgProcess.Value = nProgress;
26 }
27
28 void ProcessRoutine()
29 {
30 for (int nValue = StartFrom; nValue <= EndTo; nValue++)
31 {
32 this.Invoke(this.TheDelegate, nValue);
33 }
34 }
这个是使用线程的方法去处理,但是问题还是有的,虽然线程处理了用户界面效果的问题,但是对于大数据量处理过程中,很耗时的,用户可能会去取消自己的操作,所以我们要添加方法去对自己的线程取消。微软这个方面做的很好,它提供了ManualResetEvent的委托类,可以轻松的对线程进行取消。代码如下:
首先添加ManualResetEvent的委托:
ManualResetEvent CancelEvent = new ManualResetEvent(false);
{
btnCancelButton.Enabled = false;
CancelEvent.Set();
MyThread.Join();
lblStatus.Text = "Cancelled!";
}
修改ProcessRoutine的代码如下:
{
for (int nValue = StartFrom; nValue <= EndTo; nValue++)
{
if (CancelEvent.WaitOne(0, false) == true)
{
return;
}
this.BeginInvoke(this.TheDelegate, nValue);
Thread.Sleep(200);
}
}
另外一点,我们还可以对线程进行扩充,就是将线程的暂停和继续,这个就很简单了,就是对线程的Suspend和Resume,只要再添加按钮设置线程的Suspend和Resume就可以了。代码如下:
private void btnPause_Click(object sender, EventArgs e)
{
if (!IsThreadPaused)
{
IsThreadPaused = true;
MyThread.Suspend();
btnPause.Text = "Resume";
lblStatus.Text = "Paused!";
btnCancelButton.Enabled = false;
}
else
{
IsThreadPaused = false;
MyThread.Resume();
btnPause.Text = "Pause";
btnCancelButton.Enabled = true;
}
}