2015-02-17 8 views
6

में कार्य विलंब को रद्द करना मैं वर्तमान में .NET 4.0 को लक्षित करने वाले प्रोग्राम में .NET 4.5 की Task.Delay() विधि के लिए एक विकल्प लागू करने का प्रयास कर रहा हूं। मुझे निम्नलिखित कोड this blog पर मिला।.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 मेरे लिए काफी नए हैं। System.Threading.Timer और TaskCompletionSource मेरे लिए नया ब्रांड है (आज के रूप में), और मैं उनके साथ थोड़ा संघर्ष कर रहा हूं। सब कुछ एक तरफ, मैं सोच रहा हूं कि मैं इस कोड के लिए CancellationToken कार्यक्षमता कैसे जोड़ सकता हूं। मुझे लगता है मैं इस तरह Delay() विधि के लिए एक पैरामीटर जोड़ सकता है यह सोचते हैं हूँ:

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; 
} 

... लेकिन तब, जहां मैं टोकन जाँच और विधि से बाहर हो रही के लिए तर्क रख सकता हूं? कहीं कॉलबैक में? क्या यह भी संभव है?

+2

माइक्रोसॉफ्ट एक NuGet पैकेज [Microsoft.Bcl.Async] (https://www.nuget.org/packages/Microsoft.Bcl.Async/) प्रदान करता है जो 4.0 में से 4.5 सुविधाओं में से कई को बैकपोर्ट करता है। यदि आपके पास पैकेज है तो आप 'TaskEx.Delay()' के माध्यम से देरी का उपयोग कर सकते हैं। –

उत्तर

4

मैं अपने कोड को यथासंभव कम बदलने की कोशिश की है, लेकिन यहां एक काम उदाहरण है कि Task.Delay के रूप में एक ही तरह से व्यवहार करती है।

यह ध्यान रखना महत्वपूर्ण है कि मैं TrySetCanceled और TrySetResult का उपयोग करता हूं क्योंकि कार्य रद्द होने के बाद टाइमर समाप्त हो सकता है। आदर्श रूप में आप टाइमर को रोकना चाहते हैं।

यह भी ध्यान रखें एक रद्द काम के लिए एक 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; 
} 

फेंक अपने प्रश्न में थोड़ा अधिक पढ़ना होगा। आप Task.Delay की जरूरत है और आप तो आप http://www.nuget.org/packages/Microsoft.Bcl.Async/ यह विधि शामिल से Microsoft Async nuget पैकेज का उपयोग करना चाहिए .NET 4.0 लक्षित कर रहे हैं TaskEx.Delay

+2

यह एक अच्छा दो गुना जवाब है। मेरे पास वास्तव में माइक्रोसॉफ्ट.बीक्ल.एसिंक को पहले से ही मेरे प्रोजेक्ट में स्थापित किया गया था, लेकिन मुझे नहीं पता था कि वे टास्कएक्स के तहत उन तरीकों को छुपा रहे थे। संदर्भ के लंबे हाथ की स्पष्टीकरण और बीसीएल एक्सटेंशन पर टिप दोनों के लिए धन्यवाद। – bubbleking

2

this तरह:

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

संदर्भ में कहां जाता है? अगर यह स्पष्ट प्रतीत होता है तो मुझे माफ़ कर दो, यहां मेरे लिए कई नई अवधारणाएं हैं। – bubbleking

0

यहाँ आप एक संस्करण है कि कचरा कलेक्टर द्वारा टाइमर के निपटान से बचाता हैं

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); 
     } 
    }