2013-09-03 7 views
17

के दौरान सर्वश्रेष्ठ एसिंक्रोनस मुझे कुछ एसिंक्रोनस कोड लिखना होगा जो अनिवार्य रूप से बार-बार बात करने और डेटाबेस को प्रारंभ करने का प्रयास करता है। अक्सर पहला प्रयास विफल हो जाएगा इसलिए इसे पुनः प्रयास करने की आवश्यकता है।विधि

वर्ष के दिनों में मैं एक पैटर्न के समान इस्तेमाल किया है जाएगा:

void WaitForItToWork() 
{ 
    bool succeeded = false; 
    while (!succeeded) 
    { 
     // do work 
     succeeded = outcome; // if it worked, mark as succeeded, else retry 
     Threading.Thread.Sleep(1000); // arbitrary sleep 
    } 
} 

मैं परिवर्तन हाल ही में किए गए हैं async पैटर्न के संबंध में नेट के लिए की एक बहुत कुछ पता है तो मेरे सवाल का वास्तव में यह सबसे अच्छा है उपयोग करने के लिए विधि या async सामान की खोज करते समय यह मूल्यवान है और यदि ऐसा है तो मैं async में इस पैटर्न को कैसे कार्यान्वित करूं?

अद्यतन

बस स्पष्ट करने के लिए, मैं अतुल्यकालिक रूप से इस काम अंडे देने के लिए चाहते हैं, ताकि विधि है जो यह spawns के रूप में यह एक सेवा के निर्माता में पैदा हो जाएगा यह समाप्त होने की प्रतीक्षा की जरूरत नहीं है तो कन्स्ट्रक्टर तुरंत लौट जाना चाहिए। , जाहिरा तौर पर

async Task<bool> WaitForItToWork() 
{ 
    bool succeeded = false; 
    while (!succeeded) 
    { 
     // do work 
     succeeded = outcome; // if it worked, make as succeeded, else retry 
     await Task.Delay(1000); // arbitrary delay 
    } 
    return succeeded; 
} 

, केवल लाभ यह देना होगा आप थ्रेड पूल के अधिक कुशल उपयोग है, क्योंकि यह हमेशा देरी बनाने के लिए एक पूरी धागा ले करता है:

उत्तर

25

आप इस तरह है कि टुकड़ा refactor सकता है होता है।

outcome को प्राप्त करने के तरीके के आधार पर, async/await का उपयोग करके यह काम करने के लिए और अधिक कुशल तरीके हो सकते हैं। अक्सर आपके पास GetOutcomeAsync() जैसा कुछ हो सकता है जो एक वेब सेवा, डेटाबेस या सॉकेट कॉल को प्राकृतिक तरीके से असीमित रूप से बना देगा, तो आप केवल var outcome = await GetOutcomeAsync() करेंगे।

यह ध्यान रखना महत्वपूर्ण है कि WaitForItToWork संकलक द्वारा भागों में विभाजित किया जाएगा और await लाइन का हिस्सा असीमित रूप से जारी रहेगा। Here's शायद आंतरिक रूप से यह कैसे किया जाता है पर सबसे अच्छा स्पष्टीकरण। बात यह है कि, आमतौर पर आपके कोड के किसी बिंदु पर आपको एसिंक कार्य के परिणाम पर सिंक्रनाइज़ करना होगा। उदा .:

private void Form1_Load(object sender, EventArgs e) 
{ 
    Task<bool> task = WaitForItToWork(); 
    task.ContinueWith(_ => { 
     MessageBox.Show("WaitForItToWork done:" + task.Result.toString()); // true or false 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

आप बस इस किया जा सकता है:

private async void Form1_Load(object sender, EventArgs e) 
{ 
    bool result = await WaitForItToWork(); 
    MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false 
} 

कि हालांकि Form1_Load एक async विधि भी होगा।

[अद्यतन]

नीचे वर्णन करने के लिए करने के लिए क्या async/await वास्तव में इस मामले में करता है मेरी प्रयास है। मैंने उसी तर्क के दो संस्करण बनाए, WaitForItToWorkAsync (async/await का उपयोग करके) और WaitForItToWorkAsyncTap (TAP pattern का उपयोग async/await के बिना)। फ्रिस्ट संस्करण काफी छोटा है, दूसरे के विपरीत। इस प्रकार, जबकि async/await मोटे तौर पर संकलक की सिंटैक्टिक चीनी है, यह एसिंक्रोनस कोड को लिखने और समझने में बहुत आसान बनाता है।

// fake outcome() method for testing 
bool outcome() { return new Random().Next(0, 99) > 50; } 

// with async/await 
async Task<bool> WaitForItToWorkAsync() 
{ 
    var succeeded = false; 
    while (!succeeded) 
    { 
     succeeded = outcome(); // if it worked, make as succeeded, else retry 
     await Task.Delay(1000); 
    } 
    return succeeded; 
} 

// without async/await 
Task<bool> WaitForItToWorkAsyncTap() 
{ 
    var context = TaskScheduler.FromCurrentSynchronizationContext(); 
    var tcs = new TaskCompletionSource<bool>(); 
    var succeeded = false; 
    Action closure = null; 

    closure = delegate 
    { 
     succeeded = outcome(); // if it worked, make as succeeded, else retry 
     Task.Delay(1000).ContinueWith(delegate 
     { 
      if (succeeded) 
       tcs.SetResult(succeeded); 
      else 
       closure(); 
     }, context); 
    }; 

    // start the task logic synchronously 
    // it could end synchronously too! (e.g, if we used 'Task.Delay(0)') 
    closure(); 

    return tcs.Task; 
} 

// start both tasks and handle the completion of each asynchronously 
private void StartWaitForItToWork() 
{ 
    WaitForItToWorkAsync().ContinueWith((t) => 
    { 
     MessageBox.Show("WaitForItToWorkAsync complete: " + t.Result.ToString()); 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 

    WaitForItToWorkAsyncTap().ContinueWith((t) => 
    { 
     MessageBox.Show("WaitForItToWorkAsyncTap complete: " + t.Result.ToString()); 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

// await for each tasks (StartWaitForItToWorkAsync itself is async) 
private async Task StartWaitForItToWorkAsync() 
{ 
    bool result = await WaitForItToWorkAsync(); 
    MessageBox.Show("WaitForItToWorkAsync complete: " + result.ToString()); 

    result = await WaitForItToWorkAsyncTap(); 
    MessageBox.Show("WaitForItToWorkAsyncTap complete: " + result.ToString()); 
} 

सूत्रण पर में कुछ जानकारी। यहां स्पष्ट रूप से बनाए गए कोई अतिरिक्त धागे नहीं हैं।आंतरिक रूप से, Task.Delay() कार्यान्वयन पूल थ्रेड का उपयोग कर सकता है (मुझे संदेह है कि वे Timer Queues का उपयोग करते हैं), लेकिन इस विशेष उदाहरण (एक WinForms ऐप) में, await के बाद निरंतरता उसी यूआई थ्रेड पर होगी। अन्य निष्पादन वातावरण (जैसे एक कंसोल ऐप) में, यह एक अलग थ्रेड पर जारी रह सकता है। स्टीफन क्लेरी द्वारा आईएमओ, this articleasync/await थ्रेडिंग अवधारणाओं को समझने के लिए एक पढ़ा जाना चाहिए।

+0

क्या मैं इसे 'यह कहकर बस कॉल करूंगा। WaitForItToWork();' - async लाइब्रेरी मेरे लिए थ्रेडिंग का ख्याल रखेगी? – Chris

+1

आप इसे 'प्रतीक्षा करें। WaitForItToWork()' की तरह कॉल करेंगे, और कॉल की पूरी श्रृंखला को इसका समर्थन करने के लिए दोबारा प्रतिक्रिया दी जानी चाहिए ... मैं इसे और अधिक जानकारी शामिल करने के लिए अपने उत्तर पर विस्तृत करूंगा। – Noseratio

+0

@ क्रिस: आपको "प्रतीक्षा" कीवर्ड का उपयोग करना याद रखना चाहिए। अंगूठे का नियम: हमेशा "प्रतीक्षा" को "async" फ़ंक्शन के साथ जोड़ा जाना चाहिए। तो, आपको प्रतीक्षा करें जैसे प्रतीक्षा करें WaitForItToWork(); –

0

तो काम अतुल्यकालिक है आप के साथ की कोशिश कर सकते हैं:

async Task WaitForItToWork() 
    { 
     await Task.Run(() => 
     { 
      bool succeeded = false; 
      while (!succeeded) 
      { 
       // do work 
       succeeded = outcome; // if it worked, make as succeeded, else retry 
       System.Threading.Thread.Sleep(1000); // arbitrary sleep 
      } 
     }); 
    } 

http://msdn.microsoft.com/en-us/library/hh195051.aspx देखें।

+1

यह वही है जो मैं अपने उत्तर से बचना चाहता था। शायद, मैं खुद को कुछ याद कर रहा हूं:] – Noseratio

+0

@Noseratio Async विधि तत्काल वापस आ जाएगी जब एक प्रतीक्षा मारा जाता है। सी # कंपाइलर आपके लिए सभी थ्रेड सिंक्रनाइज़ेशन करता है। http://msdn.microsoft.com/en-us/library/vstudio/hh156528.aspx महत्वपूर्ण रूप से; "_ अभिव्यक्ति का इंतजार उस धागे को अवरुद्ध नहीं करता है जिस पर यह निष्पादित हो रहा है। इसके बजाए, यह संकलक को शेष कार्य__ – Gusdor

+0

पर निरंतरता के रूप में शेष एसिंक विधि को साइन अप करने का कारण बनता है। Chillax। तथ्य के बावजूद, स्पष्टता को सही करने या जोड़ने में कोई हानि नहीं है। मैंने अन्य पाठकों से भ्रम से बचने के लिए संपादित किया। मैंने आपको मूर्ख दिखने के लिए संपादित नहीं किया था। वह डीआरवाई सिद्धांत का उल्लंघन करेगा।/बीमार – Gusdor

0

आप वास्तव में WaitItForWork विधि की जरूरत नहीं है, बस एक डेटाबेस प्रारंभ कार्य के लिए इंतजार है:

async Task Run() 
{ 
    await InitializeDatabase(); 
    // Do what you need after database is initialized 
} 

async Task InitializeDatabase() 
{ 
    // Perform database initialization here 
} 

आपको लगता है कि तब WaitForItToWork करने के लिए कॉल आप एक Task में डेटाबेस प्रारंभ रैप करने के लिए की जरूरत है कोड के कई हिस्सों को है, तो और सभी मजदूरों में यह इंतजार है, उदाहरण के लिए:

readonly Task _initializeDatabaseTask = InitializeDatabase(); 

async Task Worker1() 
{ 
    await _initializeDatabaseTask; 
    // Do what you need after database is initialized 
} 

async Task Worker2() 
{ 
    await _initializeDatabaseTask; 
    // Do what you need after database is initialized 
} 

static async Task InitializeDatabase() 
{ 
    // Initialize your database here 
} 
0

बस एक और समाधान प्रदान

public static void WaitForCondition(Func<bool> predict) 
    { 
     Task.Delay(TimeSpan.FromMilliseconds(1000)).ContinueWith(_ => 
     { 
      var result = predict(); 
      // the condition result is false, and we need to wait again. 
      if (result == false) 
      { 
       WaitForCondition(predict); 
      } 
     }); 
    }