微软的文档中不建议以Abort的方式终止线程。比如终止线程的瞬间,FileStream没有释放,会出现问题,等等。
Framework4.0提供了标准取消模式:协作式取消(Cooperative Cancellation),工作线程会定期检查终止线程的标识。取消的时候,协作类中设置这个标识,然后等待工作线程响应。BackgroundWorker类中有类似的标识,可进行对比。
Demo 1:BackgroundWorker类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.ComponentModel;
namespace BackgroundWorkerDemo
{
class Program
{
static BackgroundWorker bw; //namespace是 ComponentModel
static void Main(string[] args)
{
bw = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
bw.DoWork += bw_DoWork; //将方法挂载到DoWork
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync();
Console.ReadLine();
if (bw.IsBusy)
bw.CancelAsync();
}
static void bw_DoWork(object sender, DoWorkEventArgs e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Argument);
Console.ResetColor();
int i = 0;
while(true)
{
if (bw.CancellationPending)
{
e.Cancel = true;
return;
}
bw.ReportProgress(i);
i++;
Thread.Sleep(100);
Console.WriteLine("DoWork progress: " + i.ToString());
if (i == 5) //可以在工作线程中退出
{
e.Cancel = true;
return;
}
}
}
static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
Console.WriteLine("you canceled,the progress quits");
}
else if (e.Error != null)
{
Console.WriteLine("Worker exception:" + e.Error.ToString());
}
else
{
Console.WriteLine("Complete :" + e.Result);
}
}
}
}
Demo 2 :开启启两个线程,分别向对应文本框中添加信息
private void button4_Click(object sender, EventArgs e)// 线程的终止,不建议使用Abort
{
//第一种方法:Abort 终止线程
//if (AddThread.IsAlive && AddThread != null)
//{
// AddThread.Abort();
//}
//if (AddThread2nd.IsAlive && AddThread2nd != null)
//{
// AddThread2nd.Abort();
//}
//第二种方法
canceler.Cancel();
canceler2nd.Cancel();
canceler.Token.Register(() =>
{
MessageBox.Show("工作线程被终止");
});
Thread1st = null;
AddThread2nd = null;
}
有一个问题,快速点击start和stop,线程停止没有问题,但是有时候却只开启了一个线程。可能是因为IsCancellationRequested的值没有及时更新为false。参考https://blog.gkarch.com/threading/part3.html中第4部分的内容,写个线程终止类,代码如下:
class RulyCanceler
{
readonly object _locker = new object();
bool cancelRequest = false;
bool IsCancellationRequest
{
get
{
lock (_locker)
{
return cancelRequest;
}
}
}
public void Cancel()
{
lock (_locker)
{
cancelRequest = true;
}
}
public void Resume()
{
cancelRequest = false;
}
public void ThrowIfCancellationRequested()
{
if (IsCancellationRequest)
{
throw new OperationCanceledException();
}
}
}
try
{
while (true)
{
canceler.ThrowIfCancellationRequested();
Thread.Sleep(10);
AddHandleNew(i);
i++;
}
}
catch (OperationCanceledException)
{
canceler.Resume();
return;
}
Resume需要放在catch(OperationCanceledException)中,也就是说catch到取消事件后,要将类中的取消标记置为false。这样才能避免反复点击start和stop出问题。