2013-06-06 3 views
5

लंबे समय के बाद से मैं निम्नलिखित टेम्पलेट का उपयोग कर कस्टम AsyncCodeActivity कक्षाएं लिख रहा हूँ:को लागू करने AsyncCodeActivities (सी # का उपयोग कर async/इंतजार है)

  1. कौन सा है:

    public sealed class MyActivity : AsyncCodeActivity<T> 
    { 
        protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state) 
        { 
         var task = new Task<T>(this.Execute, state, CancellationToken.None, TaskCreationOptions.AttachedToParent); 
         task.ContinueWith(s => callback(s)); 
         task.Start(); 
         return task; 
        } 
    
        protected override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result) 
        { 
         var task = result as Task<T>; 
         if (task.Exception != null) 
         { 
          // Error handling. Rethrow? Cancel? 
         } 
    
         return task.Result; 
        } 
    
        private T Execute(object state) 
        { 
         // Logic here 
         return default(T); 
        } 
    } 
    

    मैं इसके बारे में कुछ सवाल हैं अपवादों को संभालने का सही तरीका? Rethrowing? संदर्भ को रद्द करने के रूप में सेट करना?

  2. क्या अब इसे एसिंक/प्रतीक्षा वाक्यविन्यास के साथ लिखने का एक शानदार तरीका है?

धन्यवाद

उत्तर

13

1) आप अपने EndExecute विधि से अपवाद rethrow चाहिए।

2) मैं आपको अपना स्वयं का बेस प्रकार बनाने की सलाह देता हूं। मैं एक ऊपर से नीचे AsyncTaskCodeActivity<T> बुलाया लिखा है:

public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T> 
{ 
    protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state) 
    { 
     var task = ExecuteAsync(context); 
     var tcs = new TaskCompletionSource<T>(state); 
     task.ContinueWith(t => 
     { 
      if (t.IsFaulted) 
       tcs.TrySetException(t.Exception.InnerExceptions); 
      else if (t.IsCanceled) 
       tcs.TrySetCanceled(); 
      else 
       tcs.TrySetResult(t.Result); 

      if (callback != null) 
       callback(tcs.Task); 
     }); 

     return tcs.Task; 
    } 

    protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result) 
    { 
     var task = (Task<T>)result; 
     try 
     { 
      return task.Result; 
     } 
     catch (AggregateException ex) 
     { 
      ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); 
      throw; 
     } 
    } 

    protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context); 
} 

तुम मेरे AsyncEx पुस्तकालय का उपयोग करते हैं, इस आवरण बहुत सरल हो जाता है:

public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T> 
{ 
    protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state) 
    { 
     var task = ExecuteAsync(context); 
     return AsyncFactory<T>.ToBegin(task, callback, state); 
    } 

    protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result) 
    { 
     return AsyncFactory<T>.ToEnd(result); 
    } 

    protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context); 
} 

एक बार जब आप आधार प्रकार है, आप अपने खुद के व्युत्पन्न प्रकार परिभाषित कर सकते हैं। यहाँ एक का उपयोग करता है है async/await:

public sealed class MyActivity : AsyncTaskCodeActivity<int> 
{ 
    protected override async Task<int> ExecuteAsync(AsyncCodeActivityContext context) 
    { 
     await Task.Delay(100); 
     return 13; 
    } 
} 

और यहाँ एक है कि कार्यक्रम थ्रेड पूल के लिए CPU बाध्य काम (अपने वर्तमान टेम्पलेट के समान) है:

public sealed class MyCpuActivity : AsyncTaskCodeActivity<int> 
{ 
    protected override Task<int> ExecuteAsync(AsyncCodeActivityContext context) 
    { 
     return Task.Run(() => 13); 
    } 
} 

टिप्पणियों से अद्यतन : यहां रद्दीकरण का उपयोग करने वाला एक है। मुझे 100% यकीन नहीं है कि यह सही है, क्योंकि रद्दीकरण स्वयं ही समकालिक है, और AsyncCodeActivity<T>.Cancel के लिए अर्थशास्त्र दस्तावेज हैं (यानी, Cancel को रद्द किए गए राज्य में गतिविधि को पूरा करने की प्रतीक्षा करनी चाहिए? क्या यह सफलतापूर्वक पूर्ण करने के लिए एक गतिविधि के लिए स्वीकार्य है Cancel के बाद कहा जाता है?)।

public abstract class AsyncTaskCodeActivity<T> : AsyncCodeActivity<T> 
{ 
    protected sealed override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state) 
    { 
     var cts = new CancellationTokenSource(); 
     context.UserState = cts; 
     var task = ExecuteAsync(context, cts.Token); 
     return AsyncFactory<T>.ToBegin(task, callback, state); 
    } 

    protected sealed override T EndExecute(AsyncCodeActivityContext context, IAsyncResult result) 
    { 
     try 
     { 
      return AsyncFactory<T>.ToEnd(result); 
     } 
     catch (OperationCanceledException) 
     { 
      if (context.IsCancellationRequested) 
       context.MarkCanceled(); 
      else 
       throw; 
      return default(T); // or throw? 
     } 
    } 

    protected override void Cancel(AsyncCodeActivityContext context) 
    { 
     var cts = (CancellationTokenSource)context.UserState; 
     cts.Cancel(); 
    } 

    protected abstract Task<T> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken); 
} 
+2

शानदार उत्तर! धन्यवाद! – fra

+0

एक छोटी अतिरिक्त आवश्यकता: मुझे आवश्यक एसिंक गतिविधि को एक पिक गतिविधि में एक संभावित ट्रिगर माना जाता है, इसलिए मुझे रद्दीकरण (सुंदर) रद्दीकरण की भी आवश्यकता है ... इस तरह के परिदृश्य का समर्थन करने के लिए इसे कैसे बढ़ाया जा सकता है? धन्यवाद – fra

+0

मैं डब्ल्यूएफ रद्दीकरण से परिचित नहीं हूं, लेकिन मुझे आशा है कि आप 'ExecuteAsync' को टोकन पास करने के दौरान 'BeginExecute' (और संदर्भ में इसे सहेज लें) में' रद्दीकरण टोकन स्रोत 'बना सकते हैं। फिर संदर्भ से 'रद्दीकरण टोकन स्रोत' प्राप्त करने के लिए 'रद्द करें' ओवरराइड करें, इसे रद्द करें, और 'मार्ककैंक्ड' पर कॉल करें। –

संबंधित मुद्दे

 संबंधित मुद्दे