Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/image/5.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# 如何使用CancellationToken属性?_C#_Multithreading_Asynchronous_Concurrency_Synchronization - Fatal编程技术网

C# 如何使用CancellationToken属性?

C# 如何使用CancellationToken属性?,c#,multithreading,asynchronous,concurrency,synchronization,C#,Multithreading,Asynchronous,Concurrency,Synchronization,与前面的代码相比,我希望使用CancellationTokenSource运行代码 如中所述,如何使用它,即不引发/捕获异常?我可以使用IsCancellationRequested属性吗 我试图这样使用它: cancelToken.ThrowIfCancellationRequested(); 及 但是这在cancelToken.throwifcancellationrequest()上给出了一个运行时错误工作(CancellationToken cancelToken): System.O

与前面的代码相比,我希望使用
CancellationTokenSource
运行代码

如中所述,如何使用它,即不引发/捕获异常?我可以使用
IsCancellationRequested
属性吗

我试图这样使用它:

cancelToken.ThrowIfCancellationRequested();

但是这在
cancelToken.throwifcancellationrequest()上给出了一个运行时错误工作(CancellationToken cancelToken)

System.OperationCanceledException was unhandled
  Message=The operation was canceled.
  Source=mscorlib
  StackTrace:
       at System.Threading.CancellationToken.ThrowIfCancellationRequested()
       at _7CancellationTokens.Token.Work(CancellationToken cancelToken) in C:\xxx\Token.cs:line 33
       at _7CancellationTokens.Token.<>c__DisplayClass1.<Main>b__0() in C:\xxx\Token.cs:line 22
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

您可以按如下方式实施您的工作方法:

private static void Work(CancellationToken cancelToken)
{
    while (true)
    {
        if(cancelToken.IsCancellationRequested)
        {
            return;
        }
        Console.Write("345");
    }
}
就这样。您始终需要自己处理取消-在适当的时间退出时退出方法(以便您的工作和数据处于一致的状态)

更新:我不喜欢在(!cancelToken.IsCancellationRequested)时编写
,因为通常只有很少几个退出点可以在循环体中安全地停止执行,并且循环通常有一些逻辑条件要退出(迭代集合中的所有项等)。所以我认为最好不要把这些条件混为一谈,因为它们有不同的意图

关于避免
CancellationToken.ThrowIfCancellationRequested()

作者:

。。。正如这个答案所说,用一系列检查来替换
ThrowIfCancellationRequested
,以确保
IsCancellationRequested
正常退出。但这不仅仅是一个实现细节;这会影响可观察的行为:任务将不再以取消状态结束,而是以
RanToCompletion
结束。这不仅会影响显式状态检查,还会更微妙地影响任务链接,例如使用
ContinueWith
,具体取决于使用的
TaskContinuationOptions
。我想说,通过请求的取消来避免
是危险的建议


可以使用
throwifccancellationrequested
,而无需处理异常

ThrowIfCancellationRequested
的使用是指在
任务
中使用(而不是
线程
)。 在
任务中使用时
,您不必自己处理异常(并获取未处理的异常错误)。它将导致离开
任务
,并且
任务.IsCancelled
属性将为True。不需要异常处理

在您的特定情况下,将
线程
更改为
任务

Task t = null;
try
{
    t = Task.Run(() => Work(cancelSource.Token), cancelSource.Token);
}

if (t.IsCancelled)
{
    Console.WriteLine("Canceled!");
}

您可以使用取消令牌创建任务,当应用转到后台时,您可以取消此令牌

您可以在PCL中执行此操作

另一个解决方案是Xamarin中的用户计时器。窗体,当应用程序转到后台时停止计时器

您必须将
CancellationToken
传递给任务,任务将定期监视该令牌以查看是否请求取消

// CancellationTokenSource provides the token and have authority to cancel the token
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;  

// Task need to be cancelled with CancellationToken 
Task task = Task.Run(async () => {     
  while(!token.IsCancellationRequested) {
      Console.Write("*");         
      await Task.Delay(1000);
  }
}, token);

Console.WriteLine("Press enter to stop the task"); 
Console.ReadLine(); 
cancellationTokenSource.Cancel(); 
在这种情况下,当请求取消时,操作将结束,
任务
将处于
RanToCompletion
状态。如果您想确认您的任务已被取消
,则必须使用
ThrowIfCancellationRequested
抛出一个
操作取消异常
异常

Task task = Task.Run(async () =>             
{                 
    while (!token.IsCancellationRequested) {
         Console.Write("*");                      
         await Task.Delay(1000);                
    }           
    token.ThrowIfCancellationRequested();               
}, token)
.ContinueWith(t =>
 {
      t.Exception?.Handle(e => true);
      Console.WriteLine("You have canceled the task");
 },TaskContinuationOptions.OnlyOnCanceled);  
 
Console.WriteLine("Press enter to stop the task");                 
Console.ReadLine();                 
cancellationTokenSource.Cancel();                 
task.Wait(); 

希望这有助于更好地理解。

谢谢!这并不是从我引用的相当权威的在线文本(简而言之是《C#4.0》一书)中得出的结论。你能给我一个关于“永远”的参考吗?这来自实践和经验=)。我不记得是从哪里知道的。我使用了“you always need”,因为您实际上可以使用thread.Abort()从外部中断工作线程,但这是一种非常糟糕的做法。顺便说一句,使用CancellationToken.ThrowIfCancellationRequested()也是“自己处理取消”,这是另一种方式。你能在while(!cancelToken.IsCancellationRequested)时执行吗?@OleksandrPshenychnyy我的意思是用while(!cancelToken.IsCancellationRequested)替换while(true)。这很有帮助!谢谢@fullproof运行时没有通用的方法来取消正在运行的代码,因为运行时不够聪明,无法知道进程在哪里会被中断。在某些情况下,可以简单地退出循环,在其他情况下,需要更复杂的逻辑,即必须回滚事务,必须释放资源(例如文件句柄或网络连接)。这就是为什么没有一种神奇的方法可以在不编写代码的情况下取消任务。你所想的就像是杀死一个进程,但这不是取消,这是应用程序可能发生的最糟糕的事情之一,因为无法清理。为什么你要使用
t.Start()
而不是
Task.Run()
?@XanderLuciano:在这个例子中,没有具体的原因,而使用Task.Run()可能是更好的选择。您没有显示ThrowIfCancellationRequested有一些非常好的示例,其中包括将
CancellationTokenSource
与异步方法一起使用,将长时间运行的方法与轮询一起使用,以及使用回调。文章显示了根据您的具体情况处理令牌的选项和需要。有两个问题。首先,使用。其次,这不会像您认为的那样取消潜在的等待任务。在这种情况下,您需要将
cancelToken
传递给delay:
Task.delay(10000,cancelToken)
。通过令牌取消是合作的。它需要传递给链中您希望能够取消的每一个等待的对象。
var cancelToken = new CancellationTokenSource();
Task.Factory.StartNew(async () => {
    await Task.Delay(10000);
    // call web API
}, cancelToken.Token);

//this stops the Task:
cancelToken.Cancel(false);
// CancellationTokenSource provides the token and have authority to cancel the token
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;  

// Task need to be cancelled with CancellationToken 
Task task = Task.Run(async () => {     
  while(!token.IsCancellationRequested) {
      Console.Write("*");         
      await Task.Delay(1000);
  }
}, token);

Console.WriteLine("Press enter to stop the task"); 
Console.ReadLine(); 
cancellationTokenSource.Cancel(); 
Task task = Task.Run(async () =>             
{                 
    while (!token.IsCancellationRequested) {
         Console.Write("*");                      
         await Task.Delay(1000);                
    }           
    token.ThrowIfCancellationRequested();               
}, token)
.ContinueWith(t =>
 {
      t.Exception?.Handle(e => true);
      Console.WriteLine("You have canceled the task");
 },TaskContinuationOptions.OnlyOnCanceled);  
 
Console.WriteLine("Press enter to stop the task");                 
Console.ReadLine();                 
cancellationTokenSource.Cancel();                 
task.Wait();