2015-02-17 16 views
6

Tôi hiện đang cố gắng thực hiện thay thế cho phương thức Task.Delay() của Net 4.5 trong chương trình phải nhắm mục tiêu .Net 4.0. Tôi tìm thấy mã sau tại this blog.Hủy bỏ chậm trễ công việc trong .Net 4.0

/* You can write Task-based asynchronous methods by utilizing a TaskCompletionSource. 
A TaskCompletionSource gives you a 'slave' Task that you can manually signal. 
Calling SetResult() signals the task as complete, and any continuations kick off. */ 

void Main() 
{  
    for (int i = 0; i < 10000; i++) 
    { 
     Task task = Delay (2000); 
     task.ContinueWith (_ => "Done".Dump()); 
    } 
} 

Task Delay (int milliseconds)  // Asynchronous NON-BLOCKING method 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1); 
    return tcs.Task; 
} 

Tasks hoàn toàn mới đối với tôi. System.Threading.TimerTaskCompletionSource là thương hiệu mới đối với tôi (tính đến hôm nay) và tôi đang gặp khó khăn với họ. Tất cả những điều đó sang một bên, tôi tự hỏi làm thế nào tôi có thể thêm chức năng CancellationToken vào mã này. Tôi giả định tôi có thể thêm một tham số cho phương thức Delay() như thế này:

Task Delay (int milliseconds, CancellationToken token)  // Asynchronous NON-BLOCKING method 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    new Timer (_ => tcs.SetResult (null)).Change (milliseconds, -1); 
    return tcs.Task; 
} 

... nhưng sau đó, nơi nào tôi đặt logic để kiểm tra thẻ và nhận ra của phương pháp này? Một nơi nào đó trong cuộc gọi lại? Điều này thậm chí có thể?

+2

Microsoft cung cấp gói NuGet [Microsoft.Bcl.Async] (https://www.nuget.org/packages/Microsoft.Bcl.Async/) giúp backport nhiều trong số 4,5 tính năng bổ sung thành 4.0. Bạn có thể truy cập trễ thông qua 'TaskEx.Delay()' nếu bạn có gói. –

Trả lời

4

Tôi đã cố gắng để thay đổi mã của bạn càng ít càng tốt nhưng đây là một ví dụ làm việc mà ứng xử theo cách tương tự như Task.Delay.

Điều quan trọng cần lưu ý là tôi sử dụng TrySetCanceledTrySetResult vì Bộ hẹn giờ có thể kết thúc sau khi tác vụ bị hủy. Lý tưởng nhất là bạn muốn dừng hẹn giờ.

Cũng lưu ý một nhiệm vụ hủy sẽ ném một TaskCanceledException

static void Main(string[] args) 
{ 
    // A cancellation source that will cancel itself after 1 second 
    var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1)); 

    try 
    { 
     // This will only wait 1 second because as it will be cancelled. 
     Task t = Delay(5000, cancellationTokenSource.Token);     
     t.Wait(); 
     Console.WriteLine("The task completed"); 
    } 
    catch (AggregateException exception) 
    { 
     // Expecting a TaskCanceledException 
     foreach (Exception ex in exception.InnerExceptions) 
      Console.WriteLine("Exception: {0}", ex.Message); 
    } 
    Console.WriteLine("Done"); 
    Console.ReadLine(); 
} 

private static Task Delay(int milliseconds, CancellationToken token) 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    token.Register(() => tcs.TrySetCanceled()); 
    Timer timer = new Timer(_ => tcs.TrySetResult(null)); 
    timer.Change(milliseconds, -1);    
    return tcs.Task; 
} 

Đọc nhiều hơn một chút vào câu hỏi của bạn. Nếu bạn cần Task.Delay và bạn đang nhắm mục tiêu NET 4.0 thì bạn nên sử dụng gói NuGet Microsoft Async từ http://www.nuget.org/packages/Microsoft.Bcl.Async/ nó chứa phương pháp TaskEx.Delay

+2

Đây là câu trả lời tuyệt vời hai lần. Tôi thực sự đã có Microsoft.Bcl.Async cài đặt trong dự án của tôi, nhưng tôi đã không nhận ra họ đã ẩn những phương thức đó trong TaskEx. Cảm ơn cả lời giải thích dài về ngữ cảnh và mẹo về phần mở rộng BCL. – bubbleking

2

Giống như this:

token.Register(() => tcs.TrySetCancelled()); 
+0

Trường hợp đó đi trong bối cảnh? Tha thứ cho tôi nếu điều này có vẻ hiển nhiên, có một số khái niệm mới mẻ cho tôi ở đây. – bubbleking

0

Ở đây bạn là một phiên bản có thể ngăn chặn xử lý hẹn giờ bằng cách thu gom rác

public static Task Delay(int milliseconds, CancellationToken token) 
    { 
     var tcs = new TaskCompletionSource<object>(); 
     var timer = new OneShotTimer((t) => { 
      using ((OneShotTimer)t) 
       tcs.SetResult(null); 
     }); 
     token.Register(() => { 
      if (timer.TryCancel()) 
      { 
       using (timer) 
        tcs.SetCanceled(); 
      } 
     }); 
     timer.Start(milliseconds); 
     return tcs.Task; 
    } 


    public class OneShotTimer : IDisposable 
    { 
     private readonly object sync = new object(); 
     private readonly TimerCallback oneShotCallback; 
     private readonly Timer timer; 
     private bool isActive; 

     public OneShotTimer(TimerCallback oneShotCallback, int dueTime = Timeout.Infinite) 
     { 
      this.oneShotCallback = oneShotCallback; 
      this.isActive = dueTime != Timeout.Infinite; 
      this.timer = new Timer(callback, this, dueTime, Timeout.Infinite); 
     } 


     public void Dispose() 
     { 
      timer.Dispose(); 
     } 


     public void Start(int dueTime) 
     { 
      if (!tryChange(true, dueTime)) 
       throw new InvalidOperationException("The timer has already been started"); 
     } 


     public bool TryCancel() 
     { 
      return tryChange(false, Timeout.Infinite); 
     } 


     public bool tryChange(bool targetIsActive, int dueTime) 
     { 
      bool result = false; 
      lock (sync) 
      { 
       if (isActive != targetIsActive) 
       { 
        result = true; 
        isActive = targetIsActive; 
        timer.Change(dueTime, Timeout.Infinite); 
       } 
      } 
      return result; 
     } 


     private static void callback(object state) 
     { 
      var oneShotTimer = (OneShotTimer)state; 
      if (oneShotTimer.TryCancel()) 
       oneShotTimer.oneShotCallback(oneShotTimer); 
     } 
    } 
Các vấn đề liên quan