托管线程中的取消
.NET Framework 4为协作取消异步操作或长期运行的同步操作引入了新的统一模型。 此模型以一种名为“取消标记”的轻型对象为基础。 调用可取消操作的对象会将此标记传递给该操作。 该操作接下来可将此标记的副本传递给其他操作。 以后,创建此标记的对象可以使用它请求该操作停止正在进行的任务。 只有请求对象可以发出取消请求,每个侦听器负责识别请求并及时对请求做出响应。 下图显示了标记源与其标记的所有副本之间的关系。
操作取消:
static void CancelWithThreadPoolMiniSnippet()
{
CancellationTokenSource cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
cts.Cancel();
}
static void DoSomeWork(object obj)
{
CancellationToken token = (CancellationToken)obj;
for (int i = 0; i < 100000; i++)
{
Thread.SpinWait(5000000);
if (token.IsCancellationRequested)
{
break;
}
}
}
对象取消:
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
var obj1 = new MyCancelableObject();
var obj2 = new MyCancelableObject();
token.Register(() => obj1.Cancel());
token.Register(() => obj2.Cancel());
cts.Cancel();
侦听方式
-
轮询侦听
对于循环或递归的长时间运行的计算,可以通过定期轮询
CancellationToken.IsCancellationRequested
属性值来侦听取消请求。 如果该值为 true,则方法应尽快清理并终止。 -
通过注册回掉进行侦听
某些操作可能被阻止,因此无法及时检查取消标记的值。 对于这些情况,可以注册一个回调方法,以便在收到取消请求后取消阻止该方法。
-
使用等待句柄进行侦听
-
同时侦听多个标记
在某些情况下,侦听器可能必须同时侦听多个取消标记。 例如,可取消操作除了监视作为方法形参的实参从外部传入的标记外,可能还必须监视内部取消标记。 为此,请创建一个链接的标记源,它可以将两个或更多标记连接成一个标记
死锁
当两个线程中的每一个线程都在尝试锁定另外一个线程已锁定的资源时,就会发生死锁。 其中任何一个线程都不能继续执行
托管线程处理类的许多方法都提供了超时设定,可帮您检测到死锁。 例如,下面的代码尝试获取对当前实例的锁定。 如果在 300 毫秒内未能锁定,Monitor.TryEnter 将返回 false。
if (Monitor.TryEnter(lockObject, 300)) {
try {
// Place code protected by the Monitor here.
}
finally {
Monitor.Exit(this);
}
}
else {
// Code to execute if the attempt times out.
}
争用条件
争用条件是当程序的结果取决于两个或更多个线程中的哪一个先到达某一特定代码块时出现的一种 Bug。 多次运行程序将产生不同的结果,而且给定的任何一次运行的结果都不可预知。