26.4 协作式取消
Microsoft .NET Framework提供了一个标准的取消操作模式。这个模式是协作式的,意味着你想取消的操作必须显式地支持取消。对于长时间运行的计算限制操作来说,支持取消是一件很“棒”的事情。所以,你应该考虑为自己的计算限制操作添加取消能力。
System.Threading.CancellationTokenStore对象包含了和管理取消有关的所有状态。构造好一个CancellationTokenStore(一个引用类型)之后,可从它的Token属性获得一个或多个CancellationToken(一个值类型)实例,并传给你的操作,使那些操作可以取消。
在一个计算限制操作的循环中,可以定时调用CancellationToken的IsCancellationRequested属性,了解循环是否应该提前终止,进而终止计算限制的操作。
using System;
using System.Threading;
namespace ThreadStudy
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(o => Print(10, cts.Token));
Console.WriteLine("Main thread:Go on execute main thread...");
Thread.Sleep(5000);
//Cancell the running thread1, then exit app.
cts.Cancel();
Thread.Sleep(2000);
Console.WriteLine("Main thread:Execute completely...");
}
static void Print(int seconds,CancellationToken ts)
{
for (int i = 0; i < seconds; ++i)
{
Console.WriteLine("Thread1:Here is thread1...");
if (ts.IsCancellationRequested)
{
Console.WriteLine("Thread1:Thread1 was cancelled...");
break;
}
else
{
Console.WriteLine("Thread1:{0} seconds' tasks in thread1 have been completed...",i);
Thread.Sleep(1000);
}
}
}
}
}
Main thread:Go on execute main thread...
Thread1:Here is thread1...
Thread1:0 seconds' tasks in thread1 have been completed...
Thread1:Here is thread1...
Thread1:1 seconds' tasks in thread1 have been completed...
Thread1:Here is thread1...
Thread1:2 seconds' tasks in thread1 have been completed...
Thread1:Here is thread1...
Thread1:3 seconds' tasks in thread1 have been completed...
Thread1:Here is thread1...
Thread1:4 seconds' tasks in thread1 have been completed...
Thread1:Here is thread1...
Thread1:Thread1 was cancelled...
Main thread:Execute completely...
Press any key to continue . . .
如果要执行一个操作,并禁止它被取消,可以向该操作传递通过调用CancellationToken的静态None属性而返回的CancellationToken。这个属性返回一个特殊的CancellToken实例,它不和任何CancellationTokenStore对象关联。
如果使用某个特殊CancellationToken实例查询CancellationToken的CanBeCanceled属性,属性会返回false。相反,对于通过查询CancellationTokenSource对象的Token属性而获得的其他所有CancellationToken实例,其属性(CanBeCanceled)都会返回true。
如果愿意,可以登记一个或多个方法在取消一个CancellationTokenSource时调用。然而,每个回调方法都是用CancellationToken的Register方法来登记的。CancellationToken的register方法返回一个CancellationTokenRegistration。
using System;
using System.Threading;
namespace ThreadStudy
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(o => Print(10, cts.Token));
Console.WriteLine("Main thread:Go on execute main thread...");
Thread.Sleep(2000);
//Cancell the running thread1, then exit app.
cts.Token.Register(() => Console.WriteLine("Canceled 1"));
cts.Token.Register(() => Console.WriteLine("Canceled 2"));
cts.Cancel();
Thread.Sleep(2000);
Console.WriteLine("Main thread:Execute completely...");
}
static void Print(int seconds,CancellationToken ts)
{
for (int i = 0; i < seconds; ++i)
{
Console.WriteLine("Thread1:Here is thread1...");
if (ts.IsCancellationRequested)
{
Console.WriteLine("Thread1:Thread1 was cancelled...");
break;
}
else
{
Console.WriteLine("Thread1:{0} seconds' tasks in thread1 have been completed...",i);
Thread.Sleep(1000);
}
}
}
}
}
输出:
Main thread:Go on execute main thread...
Thread1:Here is thread1...
Thread1:0 seconds' tasks in thread1 have been completed...
Thread1:Here is thread1...
Thread1:1 seconds' tasks in thread1 have been completed...
Canceled 2
Canceled 1
Thread1:Here is thread1...
Thread1:Thread1 was cancelled...
Main thread:Execute completely...
Press any key to continue . . .
可调用Dispose从关联的CancellationTokenSource中删除一个已登记的回调。
可通过链接一组CancellationTokenSource来创建一个CancellationTokenSource对象。任何一个链接的CancellationTokenSource被取消,这个新的CancellationTokenSource对象就会被取消。
using System;
using System.Threading;
namespace ThreadStudy
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cts1 = new CancellationTokenSource();
CancellationTokenSource cts2 = new CancellationTokenSource();
CancellationTokenSource cts3 = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);
cts1.Token.Register(() => Console.WriteLine("cts1 canceled..."));
cts2.Token.Register(() => Console.WriteLine("cts2 canceled..."));
cts3.Token.Register(() => Console.WriteLine("cts3 canceled..."));
ThreadPool.QueueUserWorkItem(o => Print(10, cts1.Token));
ThreadPool.QueueUserWorkItem(o => Print(10, cts2.Token));
Console.WriteLine("Main thread:Go on execute main thread...");
Thread.Sleep(2000);
cts1.Cancel();
Thread.Sleep(2000);
Console.WriteLine("Main thread:Execute completely...");
}
static void Print(int seconds,CancellationToken ts)
{
for (int i = 0; i < seconds; ++i)
{
if (ts.IsCancellationRequested)
{
Console.WriteLine("Thread pool thread:Thread was cancelled...");
break;
}
else
{
Console.WriteLine("Thread pool thread:{0} seconds' tasks have been completed...", i);
Thread.Sleep(1000);
}
}
}
}
}
输出:
Main thread:Go on execute main thread...
Thread pool thread:0 seconds' tasks have been completed...
Thread pool thread:0 seconds' tasks have been completed...
Thread pool thread:1 seconds' tasks have been completed...
Thread pool thread:1 seconds' tasks have been completed...
Thread pool thread:2 seconds' tasks have been completed...
Thread pool thread:Thread was cancelled...
cts1 canceled...
cts3 canceled...
Thread pool thread:3 seconds' tasks have been completed...
Main thread:Execute completely...
Press any key to continue . . .