之前,我就开始关注Thread的知识。看过计算限制异步操作、I/O限制异步操作、Task、Parallel、APM、AsyncEnumerator…也做了相应的笔记。可是,具体Coding起来,还是会感到举步艰难。Why?复杂的知识让我复杂的想问题。上午,看了Jimmy Zhang的如何在30岁前年薪超过30万,说到“我发现越是真理,就越是趋于简单”。也许,最好的是简单的,关键在于,你愿不愿意,能不能这样去做。
这里,不讲Thread的基本知识了。就陆续举出几个不同方法实现的例子,比较下,会发现简明才是美。(以下例子中,没有充分考虑到performance hit,请指正!)
协作式取消 VS 线程终止
协作式取消的介绍在Thread基础-计算限制的异步操作(CLR)介绍过了,这里不再做重复。直接Coding。
这里我们需要创建一个应用程序并在一个新线程中生成素数。在这个应用程序中有停止生成素数线程的选项。(盗用下DanielWise兄的例子哈)
界面如下:
协作式实现:
/*************************************
/* Copyright (c) 2012 mumuZ
*
* Author:mumuZ
* Blog:o http://www.cnblogs.com/lnetmor/
* Email:o lnetmor@gmail.com
*
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace ThreadIsSoEasy_MonDontCare_
{
public partial class Form1 : Form
{
private Thread primeNumberThread;
public delegate void UpdateData(string returnVal);
public delegate void UpdateButtonControl(string id, string isEnabled);
private CancellationTokenSource cancellationTokenSource;
public Form1()
{
InitializeComponent();
cmdStart.Enabled = true;
cmdStop.Enabled = false;
}
private void cmdStart_Click(object sender, EventArgs e)
{
lstPrime.Items.Clear();
cancellationTokenSource = new CancellationTokenSource();
cmdStop.Enabled = true;
cmdStart.Enabled = false;
ThreadPool.QueueUserWorkItem(o => GeneratePrimeNumbers(cancellationTokenSource.Token));
}
private void cmdStop_Click(object sender, EventArgs e)
{
cancellationTokenSource.Cancel();
cmdStop.Enabled = false;
cmdStart.Enabled = true;
}
GeneratePrimeNumbers方法需要传入一个CancellationToken实例。具体实现如下:
public void GeneratePrimeNumbers(CancellationToken token)
{
long lngCounter = 2;
long lngNumber = 3;
long lngDivideByCounter;
bool blnIsPrime;
long[] PrimeArray = new long[256];
string[] args = new string[] { "2" };
UpdateData UIDel = new UpdateData(UpdateUI);
UpdateButtonControl UIControl = new UpdateButtonControl(UpdateButtonState);
//We know that the first prime is 2.
//Therefore, let's add it to the list and start from 3.
PrimeArray[1] = 2;
//lstPrime.Items.Add(2);
this.Invoke(UIDel, args);
while (lngCounter < 256)
{
blnIsPrime = true;
if (token.IsCancellationRequested)
{
args[0] = "Cancelled!";
this.Invoke(UIDel, args);
break;
}
//Try dividing this number by any already found prime
//Which is smaller then the root of this number.
for (lngDivideByCounter = 1; PrimeArray[lngDivideByCounter]
* PrimeArray[lngDivideByCounter] <= lngNumber;
lngDivideByCounter++)
{
if (lngNumber % PrimeArray[lngDivideByCounter] == 0)
{
//This is not a prime number
blnIsPrime = false;
//Exit the loop break;
}
}
//If this is a prime number then display it
if (blnIsPrime)
{
//Guess we found a new prime.
PrimeArray[lngCounter] = lngNumber;
//Increase prime found count.
lngCounter++;
//lstPrime.Items.Add(lngNumber);
args[0] = lngNumber.ToString();
this.Invoke(UIDel, args);
//Sleep 100 milliseconds.
//This will simulate the time lag and we'll get time
//to pause and resume the thread.
Thread.Sleep(100);
}
lngNumber += 2;
}
this.Invoke(UIControl, new string[] { "cmdStart", "true" });
this.Invoke(UIControl, new string[] { "cmdStop", "false" });
}
细心地朋友应该注意到GeneratePrimeNumbers方法内,我是用两个委托来修改UI的,相信大家都知道其中的道理,不多说了。下面就来看下由线程终止来实现该功能。
/*************************************
/* Copyright (c) 2012 mumuZ
*
* Author:mumuZ
* Blog:o http://www.cnblogs.com/lnetmor/
* Email:o lnetmor@gmail.com
*
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace ThreadIsSoEasy_MonDontCare_
{
public partial class ThreadAbortForm : Form
{
private Thread primeNumberThread;
public delegate void UpdateData(string returnVal);
public delegate void UpdateButtonControl(string id, string isEnabled);
public ThreadAbortForm()
{
InitializeComponent();
cmdStart.Enabled = true;
cmdStop.Enabled = false;
}
private void cmdStart_Click(object sender, EventArgs e)
{
lstPrime.Items.Clear();
primeNumberThread = new Thread(new ThreadStart(GeneratePrimeNumbers));
primeNumberThread.Start();
cmdStart.Enabled = false;
cmdStop.Enabled = true;
}
private void cmdStop_Click(object sender, EventArgs e)
{
primeNumberThread.Abort();
lstPrime.Items.Add("Cancelled!");
cmdStart.Enabled = true;
cmdStop.Enabled = false;
}
现在,GeneratePrimeNumbers方法不需要有CancellationToken了。即,内部逻辑就简略了很多。
以上两个例子孰差孰优,仁者见仁,智者见智吧。笔者还是新苗于后者,这样我的代码就可以少CancellationTokenSource 和CancellationToken两个类,并且生成素数方法GeneratePrimeNumbers的逻辑也简明了。
But, 当停止一个操作的时候,有很多后续操作需要执行,你可能需要用到CancellationToken`s Register方法,这时候使用协作式取消来的简明了吧。
代码下载
下期预告:更改UI 同步上下文TaskSchedule VS 线程池的TaskSchedule