2013-05-17 8 views
9

मुझे थ्रेड में एक लंबी प्रक्रिया ऑपरेशन निष्पादित करना है और परिणाम को फ़ंक्शन में वापस लौटना जारी रखना है। यहां मेरा कोड है:सी # कार्य फैक्ट्री टाइमआउट

Task<ProductEventArgs>.Factory.StartNew(() => 
    { 
     try 
     { 
      // long operation which return new ProductEventArgs with a list of product 

     } 
     catch (Exception e) 
     { 
      return new ProductEventArgs() { E = e }; 
     } 

    }).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext()); 

समस्या वास्तव में मेरे पास टाइमआउट नहीं है। मैं इस तरह कुछ वापस करने के लिए टाइमर रखना चाहता हूं:

new ProductEventArgs() { E = new Exception("timeout") }; 

यदि टाइमआउट पहुंच गया है। प्रतीक्षा/async का उपयोग नहीं कर सकते हैं। बहुत बहुत धन्यवाद!

उत्तर

4

इस कोड को करता है कि आप यहां क्या व्यक्त की है:

var timeout = TimeSpan.FromSeconds(5); 

var actualTask = new Task<ProductEventArgs>(() => 
{ 
    var longRunningTask = new Task<ProductEventArgs>(() => 
    { 
     try 
     { 
      Thread.Sleep(TimeSpan.FromSeconds(10)); // simulates the long running computation 
      return new ProductEventArgs(); 
     } 
     catch (Exception e) 
     { 
      return new ProductEventArgs() { E = e }; 
     } 
    }, TaskCreationOptions.LongRunning); 

    longRunningTask.Start(); 

    if (longRunningTask.Wait(timeout)) return longRunningTask.Result; 

    return new ProductEventArgs() { E = new Exception("timed out") }; 
}); 

actualTask.Start(); 

actualTask.Wait(); 

Console.WriteLine("{0}", actualTask.Result.E); // handling E 

जैसा कि आप देख longRunningTaskTaskCreationOptions.LongRunning विकल्प के साथ बनाया जाता है। इस तरह से इसके निष्पादन के लिए Thread समर्पित होगा और ThreadPool के सामान्य व्यवहार में बहुत लंबे समय तक धागे पर कब्जा करके हस्तक्षेप नहीं करेगा - जो अन्य चीज़ों जैसे i.e. UI के लिए आवश्यक होगा। यह लंबे समय तक चलने वाले कार्यों के लिए महत्वपूर्ण है

नोट: आप actualTask को ContinueWith के साथ संभाल सकते हैं लेकिन मैं यहां सार व्यक्त करना चाहता था।

+1

प्यार आप: - * मैं थोड़ा और अधिक थ्रेड प्रबंधन पर सी # पर नया हूँ और तुम मेरा दिन बना! – Rototo

1

आप समानांतर में एक Task.Delay(timeout) काम चला सकते हैं और देखते हैं कि क्या कार्य को पूरा करने के लिए पहले था (Task.WhenAny() इस मामले में बहुत आसान है):

public void FetchProduct(TimeSpan timeout) 
{ 
    var fetchTask = Task<ProductEventArgs>.Factory.StartNew(
     () => 
     { 
      try 
      { 
       // long operation which return new ProductEventArgs with a list of product 
      } 
      catch(Exception e) 
      { 
       return new ProductEventArgs() { E = e }; 
      } 
     }); 
    Task<ProductEventArgs> resultTask; 
    if(timeout != Timeout.InfiniteTimeSpan) 
    { 
     var timeoutTask = Task.Delay(timeout); 
     resultTask = Task.WhenAny(resultTask, timeoutTask).ContinueWith<ProductEventArgs>(
      t => 
      { 
       // completed task is the result of WhenAny 
       if(t.Result == fetchTask) 
       { 
        return fetchTask.Result; 
       } 
       else 
       { 
        return new ProductEventArgs() { E = new TimeoutException() }; 
       } 
      }); 
    } 
    else 
    { 
     resultTask = fetchTask; 
    } 
    resultTask.ContinueWith(x => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext()); 
} 

ध्यान दें कि यह समाधान किसी भी रद्द तर्क नहीं है, और आपका लंबा चलने वाला कार्य अभी भी चल रहा है भले ही यह समय समाप्त हो।

12

आप CancellationToken रों उपयोग करना चाहिए:

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); 
var token = cts.Token; 
Task<ProductEventArgs>.Factory.StartNew(() => 
{ 
    try 
    { 
     // occasionally, execute this line: 
     token.ThrowIfCancellationRequested(); 
    } 
    catch (OperationCanceledException) 
    { 
     return new ProductEventArgs() { E = new Exception("timeout") }; 
    } 
    catch (Exception e) 
    { 
     return new ProductEventArgs() { E = e }; 
    } 

}).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext()); 
+0

मैं पूरी तरह से इस से सहमत हूं। उम्मीद है कि ओपी ऐसा कर सकता है - मैंने जवाब दिया (संभवतः दांत) धारणा के आधार पर कि ओपी कुछ नियंत्रण को उसके नियंत्रण के साथ बुला रहा था जिसने टाइमआउट या रद्दीकरण का समर्थन नहीं किया, इसलिए मुझे लगता है कि वह ऐसा नहीं कर सका। मुझे आशा है कि वह कर सकता है। :) –

+1

आपके कोड में, मुझे केवल टोकन को स्टार्टन्यू विधि के दूसरे तर्क के रूप में देना है, है ना? क्योंकि आपने इसे नहीं लिखा था। अगर मैंने ऐसा किया है, और उदाहरण के लिए 0sec का टाइमस्टेन है, तो मेरे पास हैंडल परिणाम (x.Result) पर एक समग्र अपवाद है। धन्यवाद – Rototo

+0

आपको अपने मामले में 'रद्दीकरण टोकन' को 'स्टार्टन्यू' से पास नहीं करना चाहिए। यदि आप करते हैं (जैसा आपने देखा है), पहले से रद्द कर दिया गया 'रद्द करना टोकन' वास्तव में कार्य शुरू होने से पहले ही रद्द कर देगा। लेकिन आप नहीं चाहते कि कार्य रद्द हो जाए, इसलिए यह वह व्यवहार नहीं है जिसे आप चाहते हैं। –

3

आप StartNew विधि के लिए वापस काम वस्तु और उसके बाद उपयोगकर्ता प्रतीक्षा विधि का उपयोग कर सकते टाइमआउट निर्धारित करने के लिए।

Task<ProductEventArgs> task = Task<ProductEventArgs>.Factory.StartNew(() => {...}); 
if (!Task.Wait(new TimeSpan(0,0,1,0)) // wait for 1 minute 
{ 
    // throw exception or something else if timeout 
} 
0

बस मुख्य कार्य (सरोगेट) के भीतर एक और काम शुरू:

Task.Factory.StartNew(() => 
     { 
      // returns a string result 
      var tsk = new Task<string>(() => { return VeryImportantThingsToDo(); }); 
      try 
      { 
       tsk.Start(); 
       if (!tsk.Wait(5000)) 
        throw new TimeoutException(); 
       return tsk.Result; 
      } 
      catch (TimeoutException) 
      { 
       // Jabba Dabba Doooooooohhhhhh 
      } 

      return "<unknown>"; 
     }).ContinueWith((o) => string result = o.Result)); 
संबंधित मुद्दे