如何停止正在运行中的子线程
通过协作式取消模式
在线程函数中,你可以周期性地检查一个标志位,以确定是否应该停止线程。该标记位可以是共享变量,也可以是CancellationToken。
CancellationTokenSource 和 CancellationToken 被用来发送取消请求,而任务或线程通过检查 CancellationToken.IsCancellationRequested 属性或调用 ThrowIfCancellationRequested 方法来响应这个请求。
using System;
using System.Threading;
using System.Threading.Tasks;
//
// 方式1:使用CancellationToken,停止标志位来停止线程
//
class CancellationTokenExample
{
private CancellationTokenSource _cts = new CancellationTokenSource();
public void StartWork()
{
Task.Run(() =>
{
try
{
// 模拟长时间运行的工作,直到有取消请求时,退出while
while(!_cts.IsCancellationRequested)
{
Thread.Sleep(1000);
//如果有取消请求,也可以通过抛出异常结束线程
_cts.Token.ThrowIfCancellationRequested();
}
}
catch (OperationCanceledException)
{
//线程被取消
}
}, _cts.Token);
}
// 调用此函数来停止线程
public void StopWork()
{
_cts.Cancel();
}
}
//
// 方式2:使用共享变量,停止标志位来停止线程
//
class ThreadStopExample
{
// 停止标志
public volatile bool StopRequested { get; set; }
public void StartWork()
{
Task.Run(() =>
{
while (!StopRequested)
{
Thread.Sleep(1000); // 模拟工作
}
});
}
// 调用此函数来停止线程
public void StopWork()
{
StopRequested = true;
}
}
强制终止线程(可能存在资源不能及时释放的泄漏风险)
如果在线程中使用了一个内层的无限循环(while (true)),这将导致线程永远不会退出这个内层循环,此时要退出线程,除非执行强制停止。
using System;
using System.Threading;
using System.Threading.Tasks;
class ForceExitCaseExample
{
private CancellationTokenSource _cts;
private Task task;
public void StartWork()
{
//确保线程没有重复运行
if (task == null || (task.IsCompleted || task.IsCanceled))
{
_cts = new CancellationTokenSource();
task = Task.Run(() =>
{
//注册cancel时,执行Abort方法(不太行,偶尔报异常)
//_cts.Token.Register(Thread.CurrentThread.Abort);
//注册cancel时,执行Interrupt方法
_cts.Token.Register(Thread.CurrentThread.Interrupt);
try
{
bool workDone = false;
//模拟长时间运行的工作
Thread.Sleep(1000);
Console.WriteLine("第一阶段工作完成...");
//模拟条件长时间不满足,运行工作死循环
while (!workDone)
{
Console.WriteLine("第二阶段工作中...");
Thread.Sleep(999);
if (false)
{
workDone = true;
}
}
}
catch (Exception ex)
{
//线程被取消
Console.WriteLine($"线程异常结束:{ex.Message}");
}
}, _cts.Token);
}
}
// 调用此函数来停止线程
public void StopWork()
{
_cts.Cancel();
}
}