आप C # में एक अतुल्यकालिक विधि कैसे बनाते हैं?


196

मैंने जो भी ब्लॉग पोस्ट पढ़ी है वह आपको बताती है कि C # में एक एसिंक्रोनस विधि का उपभोग कैसे किया जाता है, लेकिन कुछ अजीब कारणों से कभी भी यह न समझाएं कि उपभोग करने के लिए अपने स्वयं के अतुल्यकालिक तरीकों का निर्माण कैसे करें। इसलिए मेरे पास अभी यह कोड है जो मेरी विधि का उपभोग करता है:

private async void button1_Click(object sender, EventArgs e)
{
    var now = await CountToAsync(1000);
    label1.Text = now.ToString();
}

और मैंने यह तरीका लिखा है CountToAsync:

private Task<DateTime> CountToAsync(int num = 1000)
{
    return Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < num; i++)
        {
            Console.WriteLine("#{0}", i);
        }
    }).ContinueWith(x => DateTime.Now);
}

यह है, का उपयोग Task.Factory, एक अतुल्यकालिक विधि लिखने का सबसे अच्छा तरीका है, या मुझे यह एक और तरीका लिखना चाहिए?


22
मैं एक सामान्य प्रश्न पूछ रहा हूं कि किसी पद्धति की संरचना कैसे की जाए। मैं बस यह जानना चाहता हूं कि अपने पहले से ही सिंक्रोनस तरीकों को अतुल्यकालिक लोगों में बदलना कहां से शुरू करें।
खालिद अबूखमेह

2
ठीक है, तो एक विशिष्ट तुल्यकालिक विधि क्या करती है , और आप इसे अतुल्यकालिक क्यों बनाना चाहते हैं ?
एरिक लिपर्ट

मान लीजिए कि मुझे फाइलों के एक समूह को संसाधित करना है और परिणाम ऑब्जेक्ट वापस करना है।
खालिद अबूखमेह

1
ठीक है, इसलिए: (1) उच्च-विलंबता ऑपरेशन क्या है: फ़ाइलों को प्राप्त करना - क्योंकि नेटवर्क धीमा हो सकता है, या जो कुछ भी - या प्रसंस्करण कर रहा है - क्योंकि यह सीपीयू गहन है, कहते हैं। और (2) आपने अभी भी यह नहीं कहा है कि आप क्यों चाहते हैं कि यह पहले स्थान पर अतुल्यकालिक हो। क्या कोई UI थ्रेड है जिसे आप ब्लॉक नहीं करना चाहते हैं, या क्या?
एरिक लिपर्ट

21
@EricLippert ऑप द्वारा दिया गया उदाहरण बहुत बुनियादी है, वास्तव में इसे जटिल होने की आवश्यकता नहीं है।
डेविड बी।

जवाबों:


226

StartNewजब तक आपको जटिलता के उस स्तर की आवश्यकता नहीं है, मैं अनुशंसा नहीं करता ।

यदि आपकी एस्किंस्ट विधि अन्य एसिंक्स विधियों पर निर्भर है, तो asyncकीवर्ड का उपयोग करना सबसे आसान तरीका है :

private static async Task<DateTime> CountToAsync(int num = 10)
{
  for (int i = 0; i < num; i++)
  {
    await Task.Delay(TimeSpan.FromSeconds(1));
  }

  return DateTime.Now;
}

यदि आपकी async विधि CPU कार्य कर रही है, तो आपको उपयोग करना चाहिए Task.Run:

private static async Task<DateTime> CountToAsync(int num = 10)
{
  await Task.Run(() => ...);
  return DateTime.Now;
}

आपको मेरा async/ awaitइंट्रो मददगार लग सकता है।


10
@ स्टेपेन: "यदि आपकी एस्किंस्ट विधि आश्रितों के लिए अन्य एसिंक्स पद्धतियां हैं" - ठीक है, लेकिन यदि ऐसा नहीं है तो क्या होगा। क्या होगा अगर कुछ कॉलबैक कोड के साथ BeginInvoke को कॉल करने वाले कुछ कोड को लपेटने की कोशिश कर रहे थे?
रिकियॉब

1
@ रीसिबोब: आपको TaskFactory.FromAsyncलपेटने के लिए उपयोग करना चाहिए BeginInvoke। निश्चित नहीं है कि आप "कॉलबैक कोड" के बारे में क्या सोचते हैं; कोड के साथ अपना प्रश्न पोस्ट करने के लिए स्वतंत्र महसूस करें।
स्टीफन क्लीयर

@ स्टेपेन: धन्यवाद - हाँ टास्कफैक्टरी.फ्रेमसिंक वही है जिसकी मुझे तलाश थी।
ऋकिबॉब 28'13

1
स्टीफन, लूप के लिए, अगला पुनरावृत्ति तुरंत कहा जाता है या Task.Delayरिटर्न के बाद ?
jp2code

3
@ jp2code: awaitएक "अतुल्यकालिक प्रतीक्षा" है, इसलिए यह कार्य Task.Delayपूरा होने के बाद वापस आने तक अगले पुनरावृत्ति के लिए आगे नहीं बढ़ता है ।
स्टीफन क्लीरी

11

यदि आप अपने तरीके के अंदर async / प्रतीक्षा का उपयोग नहीं करना चाहते हैं, लेकिन फिर भी इसे "सजाएँ" तो बाहर से प्रतीक्षित खोजशब्द का उपयोग करने में सक्षम होने के लिए, TaskCompletionSource.cs :

public static Task<T> RunAsync<T>(Func<T> function)
{ 
    if (function == null) throw new ArgumentNullException(“function”); 
    var tcs = new TaskCompletionSource<T>(); 
    ThreadPool.QueueUserWorkItem(_ =>          
    { 
        try 
        {  
           T result = function(); 
           tcs.SetResult(result);  
        } 
        catch(Exception exc) { tcs.SetException(exc); } 
   }); 
   return tcs.Task; 
}

यहाँ से और यहाँ

टास्क के साथ इस तरह के प्रतिमान का समर्थन करने के लिए, हमें टास्क फ़ेकडे और टास्क के रूप में एक मनमाने अतुल्यकालिक ऑपरेशन को संदर्भित करने की क्षमता को बनाए रखने की जरूरत है, लेकिन अंतर्निहित टास्क के नियमों के अनुसार उस टास्क के जीवनकाल को नियंत्रित करने के लिए। अतुल्यकालिक, और ऐसा करने के लिए काफी लागत नहीं है। यह टास्क कॉमप्लेक्शन सोर्स का उद्देश्य है।

मैंने देखा कि इसका उपयोग .NET स्रोत में भी किया जाता है। WebClient.cs :

    [HostProtection(ExternalThreading = true)]
    [ComVisible(false)]
    public Task<string> UploadStringTaskAsync(Uri address, string method, string data)
    {
        // Create the task to be returned
        var tcs = new TaskCompletionSource<string>(address);

        // Setup the callback event handler
        UploadStringCompletedEventHandler handler = null;
        handler = (sender, e) => HandleCompletion(tcs, e, (args) => args.Result, handler, (webClient, completion) => webClient.UploadStringCompleted -= completion);
        this.UploadStringCompleted += handler;

        // Start the async operation.
        try { this.UploadStringAsync(address, method, data, tcs); }
        catch
        {
            this.UploadStringCompleted -= handler;
            throw;
        }

        // Return the task that represents the async operation
        return tcs.Task;
    }

अंत में, मुझे निम्नलिखित भी उपयोगी लगे:

मुझे यह सवाल हर समय आता है। निहितार्थ यह है कि बाहरी संसाधन पर I / O कॉल को अवरुद्ध करने वाले कहीं न कहीं कुछ धागा होना चाहिए। तो, एसिंक्रोनस कोड अनुरोध थ्रेड को मुक्त करता है, लेकिन सिस्टम में कहीं और दूसरे थ्रेड की कीमत पर ही सही? नहीं, कदापि नहीं। अतुल्यकालिक अनुरोधों के पैमाने को समझने के लिए, मैं एक अतुल्यकालिक I / O कॉल का (सरलीकृत) उदाहरण ट्रेस करूँगा। मान लें कि किसी फ़ाइल में लिखने के लिए अनुरोध की आवश्यकता है। अनुरोध थ्रेड अतुल्यकालिक लेखन विधि को कॉल करता है। बेस क्लास लाइब्रेरी (BCL) द्वारा WriteAsync लागू किया गया है, और इसके अतुल्यकालिक I / O के लिए पूरा करने वाले बंदरगाहों का उपयोग करता है। इसलिए, WriteAsync कॉल OS के लिए एक एसिंक्रोनस फ़ाइल लिखने के रूप में पास की जाती है। OS तब ड्राइवर स्टैक के साथ संचार करता है, जो डेटा के साथ I / O अनुरोध पैकेट (IRP) में लिखता है। यही हैं जहां बातें दिलचस्प हो जाती हैं: यदि कोई डिवाइस ड्राइवर IRP को तुरंत हैंडल नहीं कर सकता है, तो उसे इसे एसिंक्रोनस रूप से हैंडल करना होगा। तो, ड्राइवर डिस्क को लिखना शुरू करने के लिए कहता है और ओएस के लिए "लंबित" प्रतिक्रिया देता है। ओएस बीसीएल के लिए "लंबित" प्रतिक्रिया पारित करता है, और बीसीएल अनुरोध-हैंडलिंग कोड के लिए एक अधूरा काम देता है। अनुरोध-हैंडलिंग कोड उस कार्य की प्रतीक्षा करता है, जो उस विधि और इतने पर से अपूर्ण कार्य देता है। अंत में, अनुरोध-हैंडलिंग कोड ASP.NET के लिए एक अधूरा कार्य वापस लौटता है, और अनुरोध थ्रेड थ्रेड पूल पर लौटने के लिए मुक्त हो जाता है। अनुरोध-हैंडलिंग कोड उस कार्य की प्रतीक्षा करता है, जो उस विधि और इतने पर से अपूर्ण कार्य देता है। अंत में, अनुरोध-हैंडलिंग कोड ASP.NET के लिए एक अधूरा कार्य वापस लौटता है, और अनुरोध थ्रेड थ्रेड पूल पर लौटने के लिए मुक्त हो जाता है। अनुरोध-हैंडलिंग कोड उस कार्य की प्रतीक्षा करता है, जो उस विधि और इतने पर से अपूर्ण कार्य देता है। अंत में, अनुरोध-हैंडलिंग कोड ASP.NET के लिए एक अधूरा कार्य वापस लौटता है, और अनुरोध थ्रेड थ्रेड पूल पर लौटने के लिए मुक्त हो जाता है।

ASP.NET पर Async / Await का परिचय

यदि लक्ष्य मापनीयता (जवाबदेही के बजाय) में सुधार करना है, तो यह सब बाहरी I / O के अस्तित्व पर निर्भर करता है जो ऐसा करने का अवसर प्रदान करता है।


1
यदि आप टास्क के ऊपर टिप्पणी देखेंगे। क्रियान्वयन जैसे कि थ्रेडपूल पर चलने के लिए निर्दिष्ट कार्य को कतार में रखता है और उस कार्य के लिए एक टास्क हैंडल देता है । आप वास्तव में ऐसा ही करते हैं। मुझे यकीन है कि Task.Run बेहतर होगा, क्योंकि यह आंतरिक रूप से कैंसलेशनटोकन को प्रबंधित करता है और शेड्यूलिंग और रन विकल्पों के साथ कुछ अनुकूलन करता है।
असुरक्षित

तो आपने क्या किया ThreadPool.QueueUserWorkWorkItem के साथ कार्य किया जा सकता है। सही, सही?
रज़वन

-1

एक विधि को अतुल्यकालिक बनाने के लिए एक बहुत ही सरल तरीका टास्क.यिल्ड () विधि का उपयोग करना है। MSDN बताता है:

आप टास्क का उपयोग कर सकते हैं। वेल्ड (); एक अतुल्यकालिक विधि में विधि को अतुल्यकालिक रूप से पूरा करने के लिए मजबूर करने के लिए।

इसे अपनी विधि की शुरुआत में डालें और फिर यह तुरंत कॉलर पर लौटेगा और बाकी विधि को दूसरे धागे पर पूरा करेगा।

private async Task<DateTime> CountToAsync(int num = 1000)
{
    await Task.Yield();
    for (int i = 0; i < num; i++)
    {
        Console.WriteLine("#{0}", i);
    }
    return DateTime.Now;
}

WinForms एप्लिकेशन में शेष विधि उसी धागे (UI थ्रेड) में पूरी होगी। Task.Yield()मामलों है कि आप यह सुनिश्चित करें कि लौटे चाहते हैं के लिए वास्तव में उपयोगी है, Taskतुरंत निर्माण पर पूरा नहीं किया जाएगा।
थियोडोर ज़ूलियास
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.