Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 链接取消令牌_C#_.net_Cancellationtokensource_Cancellation Token - Fatal编程技术网

C# 链接取消令牌

C# 链接取消令牌,c#,.net,cancellationtokensource,cancellation-token,C#,.net,Cancellationtokensource,Cancellation Token,我使用传递的取消令牌,以便我的服务可以完全关闭。服务具有不断尝试连接到其他服务的逻辑,因此令牌是打破在单独线程中运行的重试循环的好方法。我的问题是,我需要调用一个具有内部重试逻辑的服务,但如果重试失败,则在设置的时间段后返回。我想创建一个新的具有超时的取消令牌,这将为我做到这一点。问题是,我的新令牌没有链接到“主”令牌,因此当主令牌被取消时,我的新令牌仍将处于活动状态,直到超时或建立连接并返回。我想做的是将两个令牌链接在一起,这样当主令牌被取消时,我的新令牌也将被取消。我尝试使用Cancella

我使用传递的取消令牌,以便我的服务可以完全关闭。服务具有不断尝试连接到其他服务的逻辑,因此令牌是打破在单独线程中运行的重试循环的好方法。我的问题是,我需要调用一个具有内部重试逻辑的服务,但如果重试失败,则在设置的时间段后返回。我想创建一个新的具有超时的取消令牌,这将为我做到这一点。问题是,我的新令牌没有链接到“主”令牌,因此当主令牌被取消时,我的新令牌仍将处于活动状态,直到超时或建立连接并返回。我想做的是将两个令牌链接在一起,这样当主令牌被取消时,我的新令牌也将被取消。我尝试使用
CancellationTokenSource.CreateLinkedTokenSource
方法,但当我的新令牌超时时,它也取消了主令牌。是否有一种方法可以完成我需要使用令牌的操作,或者需要更改重试逻辑(可能无法轻松完成此操作)

以下是我想做的:

主令牌–传递各种功能,以便服务可以干净地关闭。 临时令牌–传递给单个函数,并在一分钟后设置为超时

如果取消主令牌,则还必须取消临时令牌


当临时令牌过期时,它不能取消主令牌。

您想使用
取消令牌源。CreateLinkedTokenSource
。它允许有一个“父”和一个“子”
CancellationTokenSource
es。下面是一个简单的例子:

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);
按预期产出:

取消子CTS
子CTS:正确
父CTS:False

取消父CTS
子CTS:正确
父CTS:对

同样,您可以使用
CancellationTokenSource.CreateLinkedTokenSource()
来完成此操作。我想尝试展示一种模式,当您想要区分取消总体任务和取消子任务而不取消总体任务时,如何使用这样的令牌

async Task MyAsyncTask(
    CancellationToken ct)
{
    // Keep retrying until the master process is cancelled.
    while (true)
    {
        // Ensure we cancel ourselves if the parent is cancelled.
        ct.ThrowIfCancellationRequested();

        var childCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
        // Set a timeout because sometimes stuff gets stuck.
        childCts.CancelAfter(TimeSpan.FromSeconds(32));
        try
        {
            await DoSomethingAsync(childCts.Token);
        }
        // If our attempt timed out, catch so that our retry loop continues.
        // Note: because the token is linked, the parent token may have been
        // cancelled. We check this at the beginning of the while loop.
        catch (OperationCancelledException) when (childCts.IsCancellationRequested)
        {
        }
    }
}
临时令牌到期时,不得取消主令牌

请注意,
MyAsyncTask()
的签名接受
CancellationToken
而不是
CancellationTokenSource
。由于该方法只能访问
CancellationToken
上的成员,因此它不能意外地取消主/父令牌。我建议您组织代码的方式应确保主任务的
CancellationTokenSource
对尽可能少的代码可见。在大多数情况下,这可以通过将
CancellationTokenSource.Token
传递给方法来完成,而不是共享对
CancellationTokenSource
的引用


我还没有调查过,但可能有一种类似于反射的方法可以强制取消
CancellationToken
,而不访问其
CancellationTokenSource
。希望这是不可能的,但如果可能的话,这将被视为不好的做法,通常不需要担心。

如果您只有
取消令牌
,而不是
取消令牌源
,那么仍然可以创建链接的取消令牌。您只需使用
Register
方法触发(伪)子项的取消:

您可以使用
CancellationTokenSource
执行任何您通常会执行的操作。例如,您可以在持续时间后取消它,甚至覆盖以前的令牌

child.CancelAfter(cancelTime);
token = child.Token;

有几个答案提到了从父令牌创建链接令牌源。如果从其他地方传递子令牌,则此模式将崩溃。相反,您可能希望从主令牌和传递给方法的令牌创建链接令牌源

从Microsoft的文档中:


通常,通过将令牌传递给方法,取消令牌是您可以访问的全部。要使用另一个答案的方法,您可能必须重新管道化所有其他方法,以便在令牌源周围传递。此方法仅允许您使用令牌。

您是完全正确的。我开始使用CancellationTokenSource.CreateLinkedTokenSource,但认为它不起作用。我忘了当令牌超时时,它会抛出一个异常。这在我的代码中被进一步捕获。这给人的印象是它没有像我预期的那样工作。通过将我的调用放入try-catch块,效果很好。@Retrocoder如果您只想捕获内部令牌,我建议在(childCts.IsCancellationRequested){}时使用
try{doSomething(ct:childCts.token);}catch(operationcanceledexception)这样的模式。您可以将其放入重试循环中,并在循环中创建子令牌源。然后,当父令牌取消时,它会一直冒泡,但当子令牌取消时,它只会重试。我无法从您的评论中判断您可能已经正确地执行了此操作;-)。此外,
operationcancelledeexception
有自己的
CancellationToken
属性,您应该使用
catch(ex)when(condition)
方式检查该属性。这样,您将确保您已捕获正确的取消。
child.CancelAfter(cancelTime);
token = child.Token;
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) {
          if (internalToken.IsCancellationRequested) {
              Console.WriteLine("Operation timed out.");
          }
          else if (externalToken.IsCancellationRequested) {
              Console.WriteLine("Cancelling per user request.");
              externalToken.ThrowIfCancellationRequested();
          }
      }
  }
}