CancellationToken 속성을 사용하는 방법은 무엇입니까?


117

RulyCanceler 클래스 에 대한 이전 코드와 비교 하여 CancellationTokenSource.

취소 토큰 에서 언급 한대로 예외를 던지거나 잡지 않고 어떻게 사용합니까? IsCancellationRequested부동산을 사용할 수 있습니까 ?

나는 이것을 다음과 같이 사용하려고 시도했다.

cancelToken.ThrowIfCancellationRequested();

try
{
  new Thread(() => Work(cancelSource.Token)).Start();
}
catch (OperationCanceledException)
{
  Console.WriteLine("Canceled!");
}

그러나 이것은 cancelToken.ThrowIfCancellationRequested();메소드에서 런타임 오류를 제공 했습니다 Work(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:

성공적으로 실행 한 코드는 새 스레드에서 OperationCanceledException을 포착했습니다.

using System;
using System.Threading;
namespace _7CancellationTokens
{
  internal class Token
  {
    private static void Main()
    {
      var cancelSource = new CancellationTokenSource();
      new Thread(() =>
      {
         try
         {
           Work(cancelSource.Token); //).Start();
         }
         catch (OperationCanceledException)
         {
            Console.WriteLine("Canceled!");
         }
         }).Start();

      Thread.Sleep(1000);
      cancelSource.Cancel(); // Safely cancel worker.
      Console.ReadLine();
    }
    private static void Work(CancellationToken cancelToken)
    {
      while (true)
      {
        Console.Write("345");
        cancelToken.ThrowIfCancellationRequested();
      }
    }
  }
}

2
docs.microsoft.com/en-us/dotnet/standard/threading/… 에는 CancellationTokenSource비동기 메서드 사용, 폴링을 사용한 장기 실행 메서드 및 콜백 사용에 대한 몇 가지 좋은 예가 있습니다.
Ehtesh Choudhury

문서에서는 특정 사례에 따라 토큰을 처리하는 데 필요한 옵션과 옵션을 보여줍니다.
Ognyan Dimitrov

답변:


140

다음과 같이 작업 방법을 구현할 수 있습니다.

private static void Work(CancellationToken cancelToken)
{
    while (true)
    {
        if(cancelToken.IsCancellationRequested)
        {
            return;
        }
        Console.Write("345");
    }
}

그게 다야. 취소는 항상 혼자서 처리해야합니다. 종료하기에 적절한시기가되면 메소드를 종료하여 작업과 데이터가 일관된 상태에 있도록합니다.

업데이트 : 나는 while (!cancelToken.IsCancellationRequested)종종 루프 본문에서 안전하게 실행을 멈출 수있는 출구 지점이 거의 없기 때문에 쓰기를 선호하지 않으며 , 루프는 일반적으로 종료 할 논리적 조건이 있습니다 (컬렉션의 모든 항목에 대해 반복). 그래서 저는 의도가 다르기 때문에 그 조건을 혼합하지 않는 것이 좋습니다.

피에 대한주의 사항 CancellationToken.ThrowIfCancellationRequested():

문제의 코멘트 에 의해 에이 몬 Nerbonne :

... 이 답변이 말했듯이 출구에 ThrowIfCancellationRequested대한 IsCancellationRequested일련 의 검사로 정상적으로 대체 됩니다. 그러나 이것은 단순한 구현 세부 사항이 아닙니다. 관찰 가능한 동작에 영향을줍니다 : 작업은 더 이상 취소 된 상태로 끝나지 않고 RanToCompletion. 그리고 그것은 명시적인 상태 검사뿐만 아니라 더 미묘하게 ContinueWithTaskContinuationOptions사용되는 것에 따라 예를 들어 작업 체인에 영향을 줄 수 있습니다 . 피하는 ThrowIfCancellationRequested것은 위험한 조언 이라고 말하고 싶습니다 .


1
감사! 이것은 내가 인용 한 온라인 텍스트를 따르지 않습니다. 꽤 권위있는 문서입니다 (책 "C # 4.0 in a Nutshell"?). "항상"에 대한 참고 자료를 제공해 주시겠습니까?
Fulproof

1
이것은 연습과 경험에서 비롯됩니다 =). 나는 이것을 어디서 알았는지 기억할 수 없다. Thread.Abort ()를 사용하여 외부에서 예외로 작업자 스레드를 실제로 중단 할 수 있기 때문에 "항상 필요"를 사용했지만 이는 매우 나쁜 습관입니다. 그건 그렇고, CancellationToken.ThrowIfCancellationRequested ()를 사용하는 것은 "취소를 스스로 처리"하는 것과는 다른 방법입니다.
Sasha

1
@OleksandrPshenychnyy 나는 while (true)을 while (! cancelToken.IsCancellationRequested)로 바꾸는 것을 의미했습니다. 이것은 도움이되었습니다! 감사!
Doug Dawson

1
@Fulproof 런타임은 프로세스가 중단 될 수있는 위치를 알만큼 똑똑하지 않기 때문에 런타임에서 실행중인 코드를 취소하는 일반적인 방법은 없습니다. 어떤 경우에는 단순히 루프를 종료하는 것이 가능합니다. 다른 경우에는 더 복잡한 논리가 필요합니다. 즉, 트랜잭션을 롤백해야하고 리소스를 해제해야합니다 (예 : 파일 핸들 또는 네트워크 연결). 이것이 코드를 작성하지 않고 작업을 취소하는 마법 같은 방법이없는 이유입니다. 당신이 생각하는 것은 프로세스를 죽이는 것과 같지만 그것은 정리할 수 없기 때문에 응용 프로그램에 발생할 수있는 최악의 일 중 하나입니다.
user3285954 2014-06-25

1
@kosist 수동으로 시작하는 작업을 취소하지 않으려는 경우 CancellationToken.None을 사용할 수 있습니다. 물론 시스템 프로세스가 종료되면 모든 것이 중단되고 CancellationToken은 이와 관련이 없습니다. 예, 작업을 취소하는 데 실제로 사용해야하는 경우에만 CancellationTokenSource를 만들어야합니다. 사용하지 않는 것을 만드는 것은 의미가 없습니다.
Sasha

26

안녕하세요.

stackoverflow에 게시 된 모든 것을 맹목적으로 신뢰해서는 안됩니다. Jens 코드의 주석이 올바르지 않습니다. 매개 변수는 예외 발생 여부를 제어하지 않습니다.

MSDN은 그 매개 변수가 무엇을 제어하는지 매우 명확합니다. 읽어 보셨습니까? http://msdn.microsoft.com/en-us/library/dd321703(v=vs.110).aspx

경우 throwOnFirstException에 해당하는 예외 즉시 처리되지 나머지 콜백 및 캔슬 조작을 방지하는 취소 호출 밖으로 전달한다. 경우 throwOnFirstException거짓,이 오버로드는 던져 예외를 집계한다AggregateException 예외를 던지는 하나의 콜백이 실행되는 다른 등록 된 콜백을 방지하지 않도록.

CancellationTokenSource토큰 자체가 아닌 Cancel이 호출되고 소스가 관리하는 각 토큰의 상태를 변경 하기 때문에 변수 이름도 잘못되었습니다 .


또한 취소 토큰의 제안 된 사용에 대한 문서 (TAP)도 살펴보십시오. docs.microsoft.com/en-us/dotnet/standard/…
Epstone

1
이것은 매우 유용한 정보이지만 질문에 전혀 대답하지 않습니다.
11nallan11

16

취소 토큰으로 Task를 생성 할 수 있으며, 백그라운드로 이동할 때이 토큰을 취소 할 수 있습니다.

PCL https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/app-lifecycle 에서이 작업을 수행 할 수 있습니다.

var cancelToken = new CancellationTokenSource();
Task.Factory.StartNew(async () => {
    await Task.Delay(10000);
    // call web API
}, cancelToken.Token);

//this stops the Task:
cancelToken.Cancel(false);

Anther 솔루션은 Xamarin.Forms의 사용자 타이머, 앱이 백그라운드로 이동할 때 타이머 중지 https://xamarinhelp.com/xamarin-forms-timer/


10

예외를 처리하지 않고 사용할 수 있습니다ThrowIfCancellationRequested !

의 사용은 (가 아님 ) ThrowIfCancellationRequested내에서 사용됩니다 . 에서 사용 하면 예외를 직접 처리 할 필요가 없습니다 (그리고 처리되지 않은 예외 오류가 발생 함). 을 떠나게 되고 속성은 True가됩니다. 예외 처리가 필요하지 않습니다.TaskThreadTaskTaskTask.IsCancelled

특정 경우에는 ThreadTask.

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

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

왜 사용 t.Start()하고 있지 Task.Run()않습니까?
Xander Luciano

1
@XanderLuciano :이 예제에는 특별한 이유가 없으며 Task.Run ()이 더 나은 선택이었을 것입니다.
Titus

5

CancellationToken취소 요청 여부를 확인하기 위해 주기적으로 토큰을 모니터링하는 태스크에을 전달해야합니다 .

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;  
Task task = Task.Run(() => {     
  while(!token.IsCancellationRequested) {
      Console.Write("*");         
      Thread.Sleep(1000);
  }
}, token);
Console.WriteLine("Press enter to stop the task"); 
Console.ReadLine(); 
cancellationTokenSource.Cancel(); 

이 경우 취소가 요청되면 작업이 종료 Task되고 RanToCompletion상태가됩니다. 당신이 인정을 받으려면 귀하의 작업이 취소되었습니다 , 당신은 사용할 필요가 ThrowIfCancellationRequested던져 OperationCanceledException예외.

Task task = Task.Run(() =>             
{                 
    while (!token.IsCancellationRequested) {
         Console.Write("*");                      
        Thread.Sleep(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(); 

이것이 더 잘 이해하는 데 도움이되기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.