2014-04-27 3 views
1

나는 모든,모니터 비동기 제한 시간과 작업 및 취소

내가 취소 해을 수 및 라이브 특정보다 더 시간을 수행하지 않는이 비동기 작업을 모니터링 할 수 있습니다.

나는 다음 코드에 대해 이미 알고있었습니다.

CancellationTokenSource l_cts = new CancellationTokenSource(timemillis); 

취소 작업을 수행합니다 (내 비동기 메소드에서 토큰을 모니터링하기 전까지). 그러나이 NOT은 나에게 WHY에 관한 어떤 정보도주지 않았 음, 시간 초과 또는 사용자 취소시겠습니까? 나는 이러한 문제를 해결하기 위해

Token.ThrowIfCancellationRequested(); 

으로 취소를 잡을하지 않았다 동안 또한, 타임 아웃 이벤트가 지연, 나는 다음과 같은 타임 아웃 프로세스를 썼다.

static async Task TestAsync(int processDelaySeconds, int cancelDelaySeconds, int timeoutDelaySeconds) 
    { 
     CancellationTokenSource l_cts = new CancellationTokenSource(); 

     // the process to monitor 
     Task l_process = new Task((state) => 
     { 
      Console.WriteLine("Process BEGIN"); 
      // dummy loop 
      for (int l_i = 0; l_i != processDelaySeconds; l_i++) 
      { 
       Thread.Sleep(1000); 
       l_cts.Token.ThrowIfCancellationRequested(); 
      } 
      Console.WriteLine("Process END"); 
     }, null, l_cts.Token); 

     // register timeout 
     RegisteredWaitHandle l_rwh = ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle, 
       (state, timedOut) => 
       { 
        if (timedOut) 
        { 
         l_cts.Cancel(); 
         Console.WriteLine("Timed out"); 
        } 
        else 
        { 
         Console.WriteLine("Cancel Signaled"); 
        } 
       }, 
       null, (int)TimeSpan.FromSeconds(timeoutDelaySeconds).TotalMilliseconds, true); 

     // cancel task 
     if (cancelDelaySeconds > 0) 
     { 
      Task l_cancel = new Task(() => 
      { 
       Thread.Sleep(TimeSpan.FromSeconds(cancelDelaySeconds)); 
       l_cts.Cancel(); 
      }); 

      l_cancel.Start(); 
     } 

     try 
     { 
      l_process.Start(); 
      await l_process; 
     } 
     catch (OperationCanceledException) 
     { 
      Console.WriteLine("Task Cancelled"); 
     } 
     finally 
     { 
      // be sure to unregister the wait handle to cancel the timeout 
      if (l_process.Status != TaskStatus.Canceled) l_rwh.Unregister(l_cts.Token.WaitHandle); 
     } 

     Console.WriteLine("Task Status is : {0}", l_process.Status); 
    } 

    static async void Tests() 
    { 
     Console.WriteLine("NORMAL PROCESS"); 
     Console.WriteLine("--------------"); 
     await TestAsync(2, 10, 10); 

     Console.WriteLine(); 
     Console.WriteLine("CANCEL"); 
     Console.WriteLine("------"); 
     await TestAsync(5, 2, 10); 

     Console.WriteLine(); 
     Console.WriteLine("TIMEOUT"); 
     Console.WriteLine("-------"); 
     await TestAsync(10, 15, 2); 
    } 

그럼 내 질문은 : 장면 뒤에 어떤 단점이나 함정이 있습니까? 더 효율적이고 효과적인 방법 ??

ps- 목표는 짧은 코드가 아닌 성능입니다. 당신의 작업이 취소되거나 시간이 초과 된 경우 알하기 위해

+0

실행을 지연 시키려면 Thread.Sleep 대신 Task.Delay를 사용해야합니다. 특히 당신이 공연을 찾고 있다면. – Stilgar

+0

아마 더 적합 : [http://programmers.stackexchange.com/](http://programmers.stackexchange.com/) – Noctis

답변

1

, 당신이 걸리는 Task.WaitAny 오버로드를 사용할 수있는 TimeSpan :

// Index will return -1 if timeout has occured, otherwise will print the index of the completed task 
var cnclToken = new CancellationTokenSource().Token 
var yourTask = Task.Run(() => { /* Do stuff */ }, cnclToken); 
var index = Task.WhenAny(new[] { yourTask }, TimeSpan.FromSeconds(1)); 

http://msdn.microsoft.com/en-us/library/dd235645(v=vs.110).aspx

+0

주석 주셔서 감사합니다, 그러나, 우리는 이것을 할 수있는 동안 Task.WhenAny {mytask, Task.Delay (ms)} WhenAny에 시간 매개 변수가있는 과부하 또는 확장이 없습니다. 귀하의 관련 링크는 WaitAny에 대한 것이므로 int를 반환하고 기다릴 수 없습니다. WhenAny 메서드는 resource의 낭비 인 2 Task에 대한 호출 결과를 반환합니다. 시간 내 줘서 고마워. –

+0

왜 자원 낭비입니까? WaitAny는 Task 또는 TimeSpan이 완료 될 때까지 기다릴 것이며 코드를 실행하는 태스크는 하나뿐입니다. –

3

당신이 사용자의 구별해야하는 경우 시간 제한 취소, CreateLinkedTokenSource :

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApp 
{ 
    internal class Program 
    { 
     // worker 
     private static void DoWork(CancellationToken token) 
     { 
      for (int i = 0; i < 1000; i++) 
      { 
       token.ThrowIfCancellationRequested(); 
       Thread.Sleep(100); // do the work item 
      } 
      token.ThrowIfCancellationRequested(); 
     } 

     // test 
     private static void Main() 
     { 
      var userCt = new CancellationTokenSource(); 
      var combinedCt = CancellationTokenSource.CreateLinkedTokenSource(
       userCt.Token); 
      combinedCt.CancelAfter(3000); // cancel in 3 seconds 

      Console.CancelKeyPress += (s, e) => 
      { 
       e.Cancel = true; 
       userCt.Cancel(); 
      }; 

      var task = Task.Run(
       () => DoWork(combinedCt.Token), 
       combinedCt.Token); 

      try 
      { 
       task.Wait(); 
      } 
      catch (AggregateException ex) 
      { 
       Console.WriteLine(ex.InnerException.Message); 
       if (task.IsCanceled) 
       { 
        if (userCt.Token.IsCancellationRequested) 
         Console.WriteLine("Cancelled by user"); 
        else if (combinedCt.Token.IsCancellationRequested) 
         Console.WriteLine("Cancelled by time-out"); 
        else 
         Console.WriteLine("Cancelled by neither user nor time-out"); 
       } 
      } 
     } 
    } 
} 

원본 코드와 관련하여 정말로 d 이 경우에는 ThreadPool.RegisterWaitForSingleObject(l_cts.Token.WaitHandle, ...)이 필요하지 않으며, 을 using과 함께 사용할 수 있도록 반환하는 메시지는 CancellationToken.Register입니다.

+0

감사합니다 .Noseratio, 요점 중 하나는 타임 아웃이 일어 났을 때입니다. 내가 '긴'차단 작업에서 막혔다면, 대신에 작업 루프 내에서 취소를 감지하기 위해 기다리는 대신 시간 초과에 대해 즉각적으로 알고 싶습니다. 나는 당신의 솔루션이 사용의 90 %에 대해 꽤 정확하고 충분히 정확하다는 것에 동의합니다. 다시 고마워. –

+0

@GuillaumePelletier, 어떤 이유로 협조 취소를 사용할 수없는 경우 Stephen Toub의 [WithCancellation] (http://blogs.msdn.com/b/pfxteam/archive/2012/10)과 같은 것을 사용할 수 있습니다. /05/how-do-i-cancel-non-cancelable-async-operations.aspx)를 참조하십시오.사용자와 타임 아웃 취소를 구분할 필요가 있다면'CreateLinkedTokenSource'와 결합 할 수 있습니다. – Noseratio