等待 CancellationToken 取消请求
Wait for request of CancellationToken cancellation
如何暂停执行直到请求取消?
var cts = new CancellationTokenSource();
Task.Run(() =>
{
// Wait for the Cancel...
Console.WriteLine("Canceled!");
});
Console.ReadKey();
cts.Cancel();
Console.ReadKey();
可以使用WaitHandle
同步等待:
static void Main()
{
var cts = new CancellationTokenSource();
Task.Run(() =>
{
// Wait for the Cancel...
cts.Token.WaitHandle.WaitOne();
Console.WriteLine("Canceled!");
});
Console.ReadKey();
cts.Cancel();
Console.ReadKey();
}
也就是说,"blocking a thread until something is cancelled" 是一种非常罕见的情况,因此您可能为这项工作使用了错误的工具。如果您需要等待某事(不是特别是取消),您可以使用 TaskCompletionSource
代替。如果您需要对取消做出反应,可以使用 CancellationToken.Register
附加回调(从而避免阻塞线程)。
CancellationTokenSource
在内部使用ManualResetEvent
,您可以等待暴露的WaitHandle
暂停执行,直到它被设置。
var cts = new CancellationTokenSource();
Task.Run(() =>
{
WaitHandle.WaitAny(new[] { cts.Token.WaitHandle });
Console.WriteLine("Canceled!");
});
Console.ReadKey();
cts.Cancel();
Console.ReadKey();
这是CancellationTokenSource
中定义的WaitHandle:
ManualResetEvent mre = new ManualResetEvent(false);
if (Interlocked.CompareExchange(ref m_kernelEvent, mre, null) != null)
{
((IDisposable)mre).Dispose();
}
// There is a ---- between checking IsCancellationRequested and setting the event.
// However, at this point, the kernel object definitely exists and the cases are:
// 1. if IsCancellationRequested = true, then we will call Set()
// 2. if IsCancellationRequested = false, then NotifyCancellation will see that the event exists, and will call Set().
if (IsCancellationRequested)
m_kernelEvent.Set();
return m_kernelEvent;
而 Token
只是 returns 来自源的句柄(有一个引用它的内部变量)。
另一种选择是注册 Token
回调并使用您自己的 ManualResetEvent
:
var cts = new CancellationTokenSource();
Task.Run(() =>
{
var mre = new ManualResetEvent(false);
var registration = cts.Token.Register(() => mre.Set());
using (registration)
{
mre.WaitOne();
Console.WriteLine("Canceled!");
}
});
Console.ReadKey();
cts.Cancel();
Console.ReadKey();
示例:https://blogs.msdn.microsoft.com/pfxteam/2009/05/22/net-4-cancellation-framework/
此代码可用于在不阻塞线程的情况下等待取消事件。
.NET6版本可以很简单,可以取消await
public async Task Run(CancellationToken cancellationToken)
{
// Simplification for the sake of example
var cts = new CancellationTokenSource();
var waitForStop = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
// IHostApplicationLifetime event can be used instead of `cts.Token`
CancellationTokenRegistration registration = cts.Token.Register(() => waitForStop.SetResult());
await using var _ = registration.ConfigureAwait(false);
await waitForStop.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
}
不会因为 cts.Cancel()
调用而抛出 TaskCanceledException
。它将允许在离开方法之前做一些事情(例如正常关闭)。但是,如果为 cancellationToken
请求取消,它将按预期抛出 TaskCanceledException
。
如果开发者需要 return 结果:
var cts = new CancellationTokenSource();
var waitForStop = new TaskCompletionSource<bool?>(TaskCreationOptions.RunContinuationsAsynchronously);
cts.Token.Register(obj =>
{
var tcs = (TaskCompletionSource<bool?>)obj!;
tcs.TrySetResult(true);
}, waitForStop);
var result = await waitForStop.Task.ConfigureAwait(false);
最简洁的方式大概是这样:
try { await Task.Delay(Timeout.Infinite, cts.Token); } catch { }
此方法依赖于捕获异常,并且 exceptions are expensive. This should not be a problem if you are awaiting tokens sporadically, but in case you are awaiting tokens in tight loops, you may want to include in your project a cheap awaiter for CancellationToken
s, as described in this article。它将允许您这样做:
await cts.Token;
如何暂停执行直到请求取消?
var cts = new CancellationTokenSource();
Task.Run(() =>
{
// Wait for the Cancel...
Console.WriteLine("Canceled!");
});
Console.ReadKey();
cts.Cancel();
Console.ReadKey();
可以使用WaitHandle
同步等待:
static void Main()
{
var cts = new CancellationTokenSource();
Task.Run(() =>
{
// Wait for the Cancel...
cts.Token.WaitHandle.WaitOne();
Console.WriteLine("Canceled!");
});
Console.ReadKey();
cts.Cancel();
Console.ReadKey();
}
也就是说,"blocking a thread until something is cancelled" 是一种非常罕见的情况,因此您可能为这项工作使用了错误的工具。如果您需要等待某事(不是特别是取消),您可以使用 TaskCompletionSource
代替。如果您需要对取消做出反应,可以使用 CancellationToken.Register
附加回调(从而避免阻塞线程)。
CancellationTokenSource
在内部使用ManualResetEvent
,您可以等待暴露的WaitHandle
暂停执行,直到它被设置。
var cts = new CancellationTokenSource();
Task.Run(() =>
{
WaitHandle.WaitAny(new[] { cts.Token.WaitHandle });
Console.WriteLine("Canceled!");
});
Console.ReadKey();
cts.Cancel();
Console.ReadKey();
这是CancellationTokenSource
中定义的WaitHandle:
ManualResetEvent mre = new ManualResetEvent(false);
if (Interlocked.CompareExchange(ref m_kernelEvent, mre, null) != null)
{
((IDisposable)mre).Dispose();
}
// There is a ---- between checking IsCancellationRequested and setting the event.
// However, at this point, the kernel object definitely exists and the cases are:
// 1. if IsCancellationRequested = true, then we will call Set()
// 2. if IsCancellationRequested = false, then NotifyCancellation will see that the event exists, and will call Set().
if (IsCancellationRequested)
m_kernelEvent.Set();
return m_kernelEvent;
而 Token
只是 returns 来自源的句柄(有一个引用它的内部变量)。
另一种选择是注册 Token
回调并使用您自己的 ManualResetEvent
:
var cts = new CancellationTokenSource();
Task.Run(() =>
{
var mre = new ManualResetEvent(false);
var registration = cts.Token.Register(() => mre.Set());
using (registration)
{
mre.WaitOne();
Console.WriteLine("Canceled!");
}
});
Console.ReadKey();
cts.Cancel();
Console.ReadKey();
示例:https://blogs.msdn.microsoft.com/pfxteam/2009/05/22/net-4-cancellation-framework/
此代码可用于在不阻塞线程的情况下等待取消事件。
.NET6版本可以很简单,可以取消await
public async Task Run(CancellationToken cancellationToken)
{
// Simplification for the sake of example
var cts = new CancellationTokenSource();
var waitForStop = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
// IHostApplicationLifetime event can be used instead of `cts.Token`
CancellationTokenRegistration registration = cts.Token.Register(() => waitForStop.SetResult());
await using var _ = registration.ConfigureAwait(false);
await waitForStop.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
}
不会因为 cts.Cancel()
调用而抛出 TaskCanceledException
。它将允许在离开方法之前做一些事情(例如正常关闭)。但是,如果为 cancellationToken
请求取消,它将按预期抛出 TaskCanceledException
。
如果开发者需要 return 结果:
var cts = new CancellationTokenSource();
var waitForStop = new TaskCompletionSource<bool?>(TaskCreationOptions.RunContinuationsAsynchronously);
cts.Token.Register(obj =>
{
var tcs = (TaskCompletionSource<bool?>)obj!;
tcs.TrySetResult(true);
}, waitForStop);
var result = await waitForStop.Task.ConfigureAwait(false);
最简洁的方式大概是这样:
try { await Task.Delay(Timeout.Infinite, cts.Token); } catch { }
此方法依赖于捕获异常,并且 exceptions are expensive. This should not be a problem if you are awaiting tokens sporadically, but in case you are awaiting tokens in tight loops, you may want to include in your project a cheap awaiter for CancellationToken
s, as described in this article。它将允许您这样做:
await cts.Token;