2013-07-07 10 views
5

मैं कुछ कोड है कि इस तरह की कुछ धीमी गति से काम करता है एक कार्य बनाता है:मैं एक कार्य कैसे बना सकता हूं जो शरीर के अंदर प्रतीक्षा करता है जो प्रतीक्षा के समय सिंक्रोनस संस्करण के समान व्यवहार करता है?

public static Task wait1() 
{ 
    return new Task(() => 
    { 
     Console.WriteLine("Waiting..."); 
     Thread.Sleep(10000); 
     Console.WriteLine("Done!"); 
    }); 
} 

वास्तविक कार्यान्वयन में, Thread.Sleep वास्तव में एक वेब सेवा कॉल किया जाएगा। मैं विधि का शरीर बदलना चाहता हूं इंतजार कर सकता हूं (इसलिए यह नेटवर्क एक्सेस/नींद के दौरान धागा का उपभोग नहीं करता है)। मेरा पहला प्रयास (संकलन त्रुटियों के शॉटगन-डीबगिंग के आधार पर) यह था:

public static Task wait2() 
{ 
    return new Task(async() => 
    { 
     Console.WriteLine("Waiting..."); 
     await Task.Delay(10000); 
     Console.WriteLine("Done!"); 
    }); 
} 

हालांकि; यह कार्य पहले जैसा ही व्यवहार नहीं करता है, क्योंकि जब मैं इसे कॉल करता हूं। वेट() पर; यह तुरंत लौटता है।

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

मुझे ऐसा करने की क्या ज़रूरत है ताकि मैं स्टार्ट और प्रतीक्षा पर एक कॉल पर प्रतीक्षा कर सकूं जिसमें कोड के अंदर प्रतीक्षा करने के लिए कोड होता है? कार्यों को बाद में एक एजेंट द्वारा कतारबद्ध और निष्पादित किया जाता है, इसलिए यह महत्वपूर्ण है कि कार्य स्वतः शुरू नहीं हुआ है।

class Program 
{ 
    static void Main(string[] args) 
    { 
     var w1 = wait1(); 
     w1.Start(); 
     w1.Wait(); // This waits 110 seconds 

     var w2 = wait2(); 
     w2.Start(); 
     w2.Wait(); // This returns immediately 
    } 

    public static Task wait1() 
    { 
     return new Task(() => 
     { 
      Console.WriteLine("Waiting..."); 
      Thread.Sleep(10000); 
      Console.WriteLine("Done!"); 
     }); 
    } 

    public static Task wait2() 
    { 
     return new Task(async() => 
     { 
      Console.WriteLine("Waiting..."); 
      await Task.Delay(10000); 
      Console.WriteLine("Done!"); 
     }); 
    } 
} 
+2

क्यों 'नया कार्य' बिल्कुल बनाते हैं? बाहरी विधि 'async' क्यों न बनाएं और इसे स्वचालित रूप से' कार्य 'वापस कर दें? यही है, 'स्थिर async कार्य प्रतीक्षा 3() {Console.WriteLine ("..."); कार्य का इंतजार। डेले (10000); Console.WriteLine ("..."); } ' –

+0

@EricLippert मैंने कोशिश की; लेकिन ऐसा लगता है कि अलग-अलग व्यवहार किया जा रहा है ... कॉल करना '.Sartart))' सिस्टम 'फेंकता है। अविश्वसनीय अपवाद अपवाद: वादे-स्टाइल कार्य पर प्रारंभ नहीं किया जा सकता है। मेरे कार्य बाद में बुलाए जाने के लिए एक कतार में जा रहे हैं; इसलिए मुझे उन्हें बाद में शुरू करने में सक्षम होना चाहिए। –

+0

क्या आपका लक्ष्य प्रतीक्षा समय तक इंतजार लाइन पर प्रतीक्षा 1/2 रोकना है? मैं इसे @fcuesta पर आपकी टिप्पणी के आधार पर पूछता हूं कि "मुझे बाद में कॉल करने में सक्षम होना चाहिए।" यदि ऐसा है, तो मुझे लगता है कि आपके द्वारा अपेक्षित निष्पादन के साथ प्रश्न को अद्यतन करने की आवश्यकता है। –

उत्तर

8

ऐसा लगता है कि यह संभव नहीं है! alexm's answer here देखें:

कार्य async तरीकों से लौटे हमेशा से रहे हैं गर्म यानी वे राज्य चल रहा है में बनाया जाता है।

:-(

मैं अपने एजेंट बनाकर इस पर काम किया है बजाय Func<Task>s कतार, और अधिभार कि किसी कार्य को प्राप्त करता है बस () => task कतार।फिर; जब डी-queing एक काम है, मैं जाँच अगर यह नहीं चल रहा है, और यदि हां, इसे शुरू:

var currentTask = currentTaskFunction(); 
if (currentTask.Status == TaskStatus.Created) 
    currentTask.Start(); 

यह इस सरल समाधान का काम करता है, तो यह करने के लिए (के लिए एक छोटे से भद्दा लगता है, क्यों पर मूल प्रतिबंध async विधियों को हमेशा गर्म बनाया जा रहा है?), लेकिन यह मेरे लिए काम करता प्रतीत होता है :-)

+2

दरअसल, यह वही है जो आप करना चाहते हैं। 'async' दुनिया में,' कार्य 'पहले से चल रहा है, इसलिए यदि आप एक कार्य शुरू करने की क्षमता चाहते हैं बाद में, उपयोग करने के लिए सही प्रकार 'Func ' –

+0

@StephenCleary मैंने अभी आपके ब्लॉग पोस्ट को पाया है जो यह करता है, और जो मैं लिख रहा था उसकी लगभग एक सटीक प्रति है! http://blog.stephencleary.com/ 2012/12/वापसी-प्रारंभ-से-aspnet-request.html हालांकि मेरा एजेंट एक लूप को एक-एक-एक समय में कूप प्रोसेसिंग में बैठता है! –

+0

मैं देख सकता हूं कि अगर यह एसिंक विधियों ने कार्यों को वापस कर दिया तो यह भ्रमित हो जाएगा शुरू नहीं हुआ; यह सिर्फ एक शर्म की बात है कि यह स्पष्ट नहीं है कि डेला में प्रतीक्षा कैसे करें yed कार्यों। –

2

आप इस रूप में लिख सकते हैं:

public static async Task Wait2() 
{ 
    Console.WriteLine("Waiting..."); 
    await Task.Delay(10000); 
    Console.WriteLine("Done!"); 
} 

सामान्य में, यह शायद ही कभी एक अच्छा विचार कभी new Task या new Task<T> उपयोग करने के लिए है। यदि आपको async/await भाषा लिखने के बजाय भाषा समर्थन का उपयोग करने के बजाय थ्रेडपूल का उपयोग करके एक कार्य लॉन्च करना होगा, तो आपको कार्य शुरू करने के लिए Task.Run का उपयोग करना चाहिए। यह कार्य को चलाने के लिए शेड्यूल करेगा (जो महत्वपूर्ण है, कार्यों को हमेशा सम्मेलनों द्वारा "गर्म" होना चाहिए)।

ध्यान दें कि ऐसा करने से आपको Task.Start पर कॉल करने की आवश्यकता नहीं है।

+0

मैं जानबूझ कर 'स्टार्ट' को कॉल कर रहा हूं। मेरे ऐप में, कार्य "कतारबद्ध" होते हैं और बाद में संसाधित होते हैं।मैं धागे को मुक्त करने के लिए प्रतीक्षा करने में सक्षम होना चाहता हूं, क्योंकि लगभग हर कार्य कतार में मध्य में एक वेब सेवा कॉल होगी; प्रतीक्षा का इंतजार करना अच्छा होगा :-) –

0

इस प्रयास करें:

public async static Task wait2() 
{ 
     Console.WriteLine("Waiting..."); 
     await Task.Delay(2000); 
     Console.WriteLine("Done!"); 
} 

लेकिन हम जानते हैं कि काम पहले से ही ऐसा करना शुरू कर दिया है जिसे आप कॉल करना नहीं है शुरू:

var w2 = wait2(); 
//w2.Start(); 
w2.Wait(); 

मुझे लगता है कि अपने wait2 समारोह के साथ समस्या यह है कि है 2 कार्य बना रहा है, new Task(...) में से एक और दूसरा Task.Delay() में। आप पहले के लिए इंतजार कर रहे हैं, लेकिन आप भीतर की प्रतीक्षा नहीं कर रहे हैं।

+0

मुझे बाद में 'स्टार्ट' कॉल करने में सक्षम होना चाहिए, इसलिए यह काम नहीं करेगा :(मुझे संदेह है कि आप कई कार्यों के बारे में सही हो सकते हैं, लेकिन ऐसा लगता है कि बाहरी कार्य काम के पूरे ब्लॉक को शामिल नहीं करता है :( –

1

यह समझने में आपकी सहायता के लिए कि async/await अनिवार्य रूप से एक नया धागा नहीं बनाता है बल्कि यह कोड के उस हिस्से को शेड्यूल करता है समय पर एक उपलब्ध बिंदु पर भाग गया।

जब आप नया कार्य (async() => ...) बनाते हैं तो आपके पास एक कार्य है जो एसिंक विधि चलाता है। जब उस आंतरिक एसिंक विधि का इंतजार होता है तो 'नया कार्य' पूरा माना जाता है क्योंकि शेष शेष निर्धारित किया गया है। इंतजार कमांड से पहले 'नया कार्य' में बेहतर कोड को समझने में मदद करने के लिए (बहुत कुछ चाहते थे)। आवेदन समाप्त होने से पहले यह सब निष्पादित हो जाएगा और एक बार प्रतीक्षा की जा रही है कि कार्य विश्वास करेगा कि यह पूरा हो गया है। फिर यह आवेदन देता है और आवेदन से बाहर निकलता है।

इससे बचने का सबसे अच्छा तरीका यह है कि आप अपने कार्य के अंदर कोई कार्य या एसिंक विधियां न रखें।

एसिंक कीवर्ड और विधि से प्रतीक्षा कीवर्ड को हटाएं और यह अपेक्षित के रूप में काम करेगा।

यदि आप उससे परिचित हैं तो यह कॉलबैक बनाने जैसा ही है।

void MethodAsync(Action callback) 
{ 
    //...some code 
    callback?.Invoke(); 
} 

//using this looks like this. 
MethodAsync(() => { /*code to run when complete */}); 

// यह

Task MethodAsync() 
{ 
    //... some code here 
} 

//using it 

await MethodAsync(); 
/*code to run when complete */ 

को समझने के लिए बात के रूप में ही आप एक काम के भीतर एक नया कार्य बना रहे हैं कि मूल रूप से है। तो प्रतीक्षा कीवर्ड पर आंतरिक 'कॉलबैक' बनाया जा रहा है।

तुम हो कोड इस तरह दिखता है ..

void MethodAsync(Action callback) 
{ 
    //some code to run 
    callback?.Invoke(); // <- this is the await keyword 
    //more code to run.. which happens after we run whoever is 
    //waiting on callback 
} 

वहाँ स्पष्ट रूप से याद आ रही कोड है। अगर यह समझ में नहीं आता है तो कृपया मुझसे संपर्क करने में संकोच न करें और मैं सहायता करूंगा। async/प्रतीक्षा (चीजों को सरल बनाने के लिए मतलब) एक जानवर है जो आपके सिर को पहले लपेटने के लिए है। इसके बाद आप इसे प्राप्त करेंगे तो लिनक के बाद से यह सी # में आपकी पसंदीदा चीज़ होगी। : पी

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

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