如何创建分层的 CancellationTokenSource?
How to Create a Hierarchical CancellationTokenSource?
在我们的项目中,我们决定在CancellationToken
的帮助下为用户提供取消机制。
由于项目中的作品结构,我需要一个分层取消机制。通过分层,我的意思是父源取消会导致所有子源被递归取消,但子源取消不会传播到父源。
.NET 中是否有开箱即用的此类选项?如果不是,我不确定向父代币注册一个委托是否足够,或者应该给予进一步的考虑。
是的,此功能开箱即用。查看 CancellationTokenSource.CreateLinkedTokenSource
方法。
Creates a CancellationTokenSource
that will be in the canceled state when any of the source tokens are in the canceled state.
示例:
using var parentCts = new CancellationTokenSource();
using var childrenCts = CancellationTokenSource
.CreateLinkedTokenSource(parentCts.Token);
parentCts.Cancel(); // Cancel the children too
childrenCts.Cancel(); // Doesn't affect the parent
基于the source code中Linked2CancellationTokenSource
的实现,我得出了这个实现:
public class HierarchicalCancellationTokenSource : CancellationTokenSource
{
private readonly CancellationTokenRegistration _parentReg;
public HierarchicalCancellationTokenSource(CancellationToken parentToken)
{
this._parentReg = parentToken.Register(
static s => ((CancellationTokenSource)s).Cancel(false),
this,
useSynchronizationContext: false);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
this._parentReg.Dispose();
}
base.Dispose(disposing);
}
}
还有一个演示:
CancellationTokenSource[] CreateChildSources(CancellationTokenSource parentSource) =>
Enumerable.Range(0, 2)
.Select(_ => new HierarchicalCancellationTokenSource(parentSource.Token))
.ToArray();
var rootSource = new CancellationTokenSource();
var childSources = CreateChildSources(rootSource);
var grandChildSources = childSources.SelectMany(CreateChildSources).ToArray();
var allTokens = new[] { rootSource.Token }
.Concat(childSources.Select(s => s.Token))
.Concat(grandChildSources.Select(s => s.Token))
.ToArray();
for (int i = 0; i < allTokens.Length; i++)
{
allTokens[i].Register(
i => Console.WriteLine(
$"{new string('+', (int)Math.Log2((int)i))}{i} canceled."),
i + 1);
}
rootSource.Cancel();
/* Output:
1 canceled.
+3 canceled.
++7 canceled.
++6 canceled.
+2 canceled.
++5 canceled.
++4 canceled.
*/
在我们的项目中,我们决定在CancellationToken
的帮助下为用户提供取消机制。
由于项目中的作品结构,我需要一个分层取消机制。通过分层,我的意思是父源取消会导致所有子源被递归取消,但子源取消不会传播到父源。
.NET 中是否有开箱即用的此类选项?如果不是,我不确定向父代币注册一个委托是否足够,或者应该给予进一步的考虑。
是的,此功能开箱即用。查看 CancellationTokenSource.CreateLinkedTokenSource
方法。
Creates a
CancellationTokenSource
that will be in the canceled state when any of the source tokens are in the canceled state.
示例:
using var parentCts = new CancellationTokenSource();
using var childrenCts = CancellationTokenSource
.CreateLinkedTokenSource(parentCts.Token);
parentCts.Cancel(); // Cancel the children too
childrenCts.Cancel(); // Doesn't affect the parent
基于the source code中Linked2CancellationTokenSource
的实现,我得出了这个实现:
public class HierarchicalCancellationTokenSource : CancellationTokenSource
{
private readonly CancellationTokenRegistration _parentReg;
public HierarchicalCancellationTokenSource(CancellationToken parentToken)
{
this._parentReg = parentToken.Register(
static s => ((CancellationTokenSource)s).Cancel(false),
this,
useSynchronizationContext: false);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
this._parentReg.Dispose();
}
base.Dispose(disposing);
}
}
还有一个演示:
CancellationTokenSource[] CreateChildSources(CancellationTokenSource parentSource) =>
Enumerable.Range(0, 2)
.Select(_ => new HierarchicalCancellationTokenSource(parentSource.Token))
.ToArray();
var rootSource = new CancellationTokenSource();
var childSources = CreateChildSources(rootSource);
var grandChildSources = childSources.SelectMany(CreateChildSources).ToArray();
var allTokens = new[] { rootSource.Token }
.Concat(childSources.Select(s => s.Token))
.Concat(grandChildSources.Select(s => s.Token))
.ToArray();
for (int i = 0; i < allTokens.Length; i++)
{
allTokens[i].Register(
i => Console.WriteLine(
$"{new string('+', (int)Math.Log2((int)i))}{i} canceled."),
i + 1);
}
rootSource.Cancel();
/* Output:
1 canceled.
+3 canceled.
++7 canceled.
++6 canceled.
+2 canceled.
++5 canceled.
++4 canceled.
*/