Ret*_*der 32 .net c# cancellationtokensource cancellation-token
我使用传递的取消令牌,以便可以干净地关闭我的服务.该服务具有不断尝试连接到其他服务的逻辑,因此令牌是打破在单独线程中运行的这些重试循环的好方法.我的问题是我需要调用一个具有内部重试逻辑的服务,但是如果重试失败则在一段时间后返回.我想创建一个带有超时的新取消令牌,这将为我做到这一点.这个问题是我的新令牌没有链接到"主"令牌,所以当主令牌被取消时,我的新令牌仍然有效,直到它超时或建立连接并返回.我想要做的是将两个令牌链接在一起,这样当主要的一个被取消时,我的新一个也会取消.我尝试使用该CancellationTokenSource.CreateLinkedTokenSource
方法但是当我的新令牌超时时,它也取消了主令牌.有没有办法做我需要做的令牌,或者它需要更改重试逻辑(可能不会轻易做到这一点)
这是我想要做的:
主令牌 - 传递各种功能,以便服务可以干净地关闭.临时令牌 - 传递给单个函数并在一分钟后设置为超时
如果取消主令牌,则还必须取消临时令牌.
当临时令牌到期时,它不得取消主令牌.
i3a*_*non 56
你想用CancellationTokenSource.CreateLinkedTokenSource
.它允许拥有"父母"和"孩子" CancellationTokenSource
.这是一个简单的例子:
var parentCts = new CancellationTokenSource();
var childCts = CancellationTokenSource.CreateLinkedTokenSource(parentCts.Token);
childCts.CancelAfter(1000);
Console.WriteLine("Cancel child CTS");
Thread.Sleep(2000);
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);
Console.WriteLine();
parentCts.Cancel();
Console.WriteLine("Cancel parent CTS");
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);
Run Code Online (Sandbox Code Playgroud)
按预期输出:
取消子CTS
Child CTS:True
Parent CTS:False取消父CTS
Child CTS:True
Parent CTS:True
正如i3arnon 已经回答的那样,您可以使用CancellationTokenSource.CreateLinkedTokenSource()
. 我想尝试展示一种模式,当您想要区分取消整个任务和取消子任务而不取消整个任务时,如何使用此类标记。
async Task MyAsyncTask(\n CancellationToken ct)\n{\n // Keep retrying until the master process is cancelled.\n while (true)\n {\n // Ensure we cancel ourselves if the parent is cancelled.\n ct.ThrowIfCancellationRequested();\n\n using var childCts = CancellationTokenSource.CreateLinkedTokenSource(ct);\n // Set a timeout because sometimes stuff gets stuck.\n childCts.CancelAfter(TimeSpan.FromSeconds(32));\n try\n {\n await DoSomethingAsync(childCts.Token);\n }\n // If our attempt timed out, catch so that our retry loop continues.\n // Note: because the token is linked, the parent token may have been\n // cancelled. We check this at the beginning of the while loop.\n catch (OperationCancelledException) when (childCts.IsCancellationRequested)\n {\n }\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n\n当临时令牌过期时,不得取消主令牌。
\n
请注意,MyAsyncTask()
\xe2\x80\x99s 签名接受CancellationToken
而不是CancellationTokenSource
. 由于该方法只能访问 上的成员CancellationToken
,因此它不会意外取消主令牌/父令牌。我建议您以这样的方式组织代码,以使CancellationTokenSource
主任务对尽可能少的代码可见。在大多数情况下,这可以通过传递CancellationTokenSource.Token
给方法而不是共享对CancellationTokenSource
.
我没有调查过,但可能有一种方法可以通过反射之类的方法强行取消 a 而CancellationToken
无需访问其CancellationTokenSource
. 希望这是不可能的,但如果可能的话,这将被认为是不好的做法,并且通常不需要担心。
如果您所拥有的只是 a CancellationToken
,而不是 a CancellationTokenSource
,那么仍然可以创建链接的取消令牌。您只需使用该Register
方法来触发(伪)孩子的取消:
var child = new CancellationTokenSource();
token.Register(child.Cancel);
Run Code Online (Sandbox Code Playgroud)
您可以执行通常使用CancellationTokenSource
. 例如,您可以在一段时间后取消它,甚至覆盖之前的令牌。
child.CancelAfter(cancelTime);
token = child.Token;
Run Code Online (Sandbox Code Playgroud)
几个答案提到从父令牌创建链接令牌源。如果您从其他地方传递子令牌,则此模式就会崩溃。相反,您可能希望从主令牌和传递给方法的令牌创建链接令牌源。
public void DoWork(CancellationToken externalToken)
{
// Create a new token that combines the internal and external tokens.
this.internalToken = internalTokenSource.Token;
this.externalToken = externalToken;
using (CancellationTokenSource linkedCts =
CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken))
{
try {
DoWorkInternal(linkedCts.Token);
}
catch (OperationCanceledException) when (linkedCts.Token.IsCancellationRequested){
if (internalToken.IsCancellationRequested) {
Console.WriteLine("Operation timed out.");
}
else if (externalToken.IsCancellationRequested) {
Console.WriteLine("Cancelling per user request.");
externalToken.ThrowIfCancellationRequested();
}
}
catch (Exception ex)
{
//standard error logging here
}
}
}
Run Code Online (Sandbox Code Playgroud)
通常,在将令牌传递给方法时,您只能访问取消令牌。要使用其他答案的方法,您可能必须重新管道所有其他方法以传递令牌源。此方法允许您仅使用令牌。