2012-05-23 3 views
4

Async Targeting Pack의 릴리스에서는 ILSpy을 사용하여 어떤 Task-based Asynchronous Pattern (TAP) 확장 메서드가 제공되었는지 확인했습니다 (일부는 이미 VS2010에서 사용하기 위해 이미 구현했습니다). 나는 CancellationTokenSource에 대한 .CancelAfter(TimeSpan) 메쏘드를 발견했다. (.NET 4.0의 비동기 타겟팅 팩에서 확장 메소드이지만 .NET 4.5에서는 인스턴스 메쏘드이다.) 다양한 작업을위한 타임 아웃을 구현하는 좋은 방법이 될 수 있다고 생각했다. 기본적으로 제한 시간은 없지만 취소를 지원하십시오.CancellationTokenSource.CancelAfter()가 새 는가?

비동기 타겟팅 팩에서 구현을 살펴보면 연결된 Task이 완료되거나 취소되면 타이머가 계속 실행되는 것으로 보입니다.

/// <summary>Cancels the <see cref="T:System.Threading.CancellationTokenSource" /> after the specified duration.</summary> 
/// <param name="source">The CancellationTokenSource.</param> 
/// <param name="dueTime">The due time in milliseconds for the source to be canceled.</param> 
public static void CancelAfter(this CancellationTokenSource source, int dueTime) 
{ 
    if (source == null) 
    { 
     throw new NullReferenceException(); 
    } 
    if (dueTime < -1) 
    { 
     throw new ArgumentOutOfRangeException("dueTime"); 
    } 
    Timer timer = new Timer(delegate(object self) 
    { 
     ((IDisposable)self).Dispose(); 
     try 
     { 
      source.Cancel(); 
     } 
     catch (ObjectDisposedException) 
     { 
     } 
    }); 
    timer.Change(dueTime, -1); 
} 

의 내가 자주 사용하는 TAP 기반 작업에 대한 시간 제한을 제공하기 위해이 방법을 사용하고 .CancelAfter()로 포장한다고 가정 해 봅시다. 이제 사용자가 5 분 (300 초)의 시간 제한 값을 제공하고이 작업을 초당 100 회 호출한다고 가정합니다.이 작업은 모두 수 밀리 초 후에 성공적으로 완료됩니다. 초당 100 회의 호출이 300 초가 지나면 작업이 오래 전에 성공적으로 완료되었지만 모든 작업에서 30,000 개의 실행 타이머가 누적됩니다. 그들은 결국 위의 위임을 실행하고 위의 도우미를 실행하게되며 이는 아마도 ObjectDisposedException 등을 던질 것입니다.

이것은 다소 누설적이고 확장 불가능한 동작이 아닙니까? 시간 제한을 구현할 때 Task/TaskEx.Delay(TimeSpan, CancellationToken)을 사용했으며 관련 작업이 끝나면 .Delay()을 취소하여 타이머를 중지하고 처리하도록했습니다 (결국 IDisposable이며 관리되지 않는 리소스가 포함되어 있음). 이 정리는 과도하게 열광적입니까? 수만 개의 타이머를 동시에 실행하는 데 드는 비용 (나중에 수십만 가지 예외가 발생 함)은 일반 응용 프로그램의 성능에 실제로 중요하지 않습니까? .CancelAfter()의 오버 헤드 및 누출은 실제 작업과 비교할 때 거의 항상 소극적이며 일반적으로 무시해야합니까?

+1

여러 가지 작업에서 동일한 CTS를 사용할 수 있고 CTS가 작업 상태를 알 수 없기 때문에 어떤 방식 으로든 누출이 있습니다. 또한 타이머가 실행되는 동안 새 CTS를 사용할 수 있습니다. – svick

답변

7

그냥 시도해보고 한계까지 밀어 넣고 어떻게되는지 확인합니다. 나는 1000 만 타이머를 가진 90 MB가 넘는 작업 세트를 얻을 수 없습니다. System.Threading.Timer 매우 저렴합니다.

using System; 
using System.Threading; 

class Program { 
    public static int CancelCount; 
    static void Main(string[] args) { 
     int count = 1000 * 1000 * 10; 
     for (int ix = 0; ix < count; ++ix) { 
      var token = new CancellationTokenSource(); 
      token.CancelAfter(500); 
     } 
     while (CancelCount < count) { 
      Thread.Sleep(100); 
      Console.WriteLine(CancelCount); 
     } 
     Console.WriteLine("done"); 
     Console.ReadLine(); 
    } 
} 

static class Extensions { 
    public static void CancelAfter(this CancellationTokenSource source, int dueTime) { 
     Timer timer = new Timer(delegate(object self) { 
      Interlocked.Increment(ref Program.CancelCount); 
      ((IDisposable)self).Dispose(); 
      try { 
       source.Cancel(); 
      } 
      catch (ObjectDisposedException) { 
      } 
     }); 
     timer.Change(dueTime, -1); 
    } 
} 
+0

실제로 그렇게 보입니다. 감사! –

+0

타이머는 하드웨어 타이머에서 다중화 된 Win32 타이머 풀을 사용합니다. 그들은 모두 날씬한 콜백입니다. –

관련 문제