您可以创建一个额外的任务,当取消令牌的等待句柄发出信号时返回:
var factory = new CancellationTokenSource();
var token = factory.Token;
await Task.WhenAny(
Task.Run(() => token.WaitHandle.WaitOne()),
myTask());
(但是,请注意,这虽然简单,但确实会消耗额外的线程,这显然不理想。请参阅稍后的不使用额外线程的替代解决方案。)
如果您想检查哪个任务已完成,则必须在调用之前保留任务的副本WhenAny()
因此您可以将它们与返回值进行比较,例如:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main()
{
var factory = new CancellationTokenSource(1000); // Change to 3000 for different result.
var token = factory.Token;
var task = myTask();
var result = await Task.WhenAny(
Task.Run(() => token.WaitHandle.WaitOne()),
task);
if (result == task)
Console.WriteLine("myTask() completed");
else
Console.WriteLine("cancel token was signalled");
}
static async Task myTask()
{
await Task.Delay(2000);
}
}
}
如果您不想浪费整个线程等待取消令牌发出信号,您可以使用CancellationToken.Register()
注册一个回调,您可以使用它设置结果TaskCompletionSource
:
(从这里举起)
public static Task WhenCanceled(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).SetResult(true), tcs);
return tcs.Task;
}
然后您可以按如下方式使用它:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main()
{
var factory = new CancellationTokenSource(1000);
var token = factory.Token;
var task = myTask();
var result = await Task.WhenAny(
WhenCanceled(token),
task);
if (result == task)
Console.WriteLine("myTask() completed");
else
Console.WriteLine("cancel token was signalled");
}
public static Task WhenCanceled(CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).SetResult(true), tcs);
return tcs.Task;
}
static async Task myTask()
{
await Task.Delay(2000);
}
}
}
对于一般情况,这是一种更好的方法。