Async / प्रतीक्षारत BackgroundWorker बनाम


162

पिछले कुछ दिनों में मैंने .net 4.5 और c # 5 की नई विशेषताओं का परीक्षण किया है।

मुझे इसकी नई async / प्रतीक्षा सुविधाएँ पसंद हैं। इससे पहले मैंने बैकग्राउंड UI के साथ बैकग्राउंड प्रोसेस को संभालने के लिए BackgroundWorker का इस्तेमाल किया था ।

मेरा सवाल यह है: इन अच्छी नई विशेषताओं के होने के बाद, मुझे कब async / प्रतीक्षा का उपयोग करना चाहिए और कब एक BackgroundWorker ? दोनों के लिए सामान्य परिदृश्य कौन से हैं?



दोनों अच्छे हैं, लेकिन अगर आपके पुराने कोड के साथ काम करना जो बाद में .net संस्करण में माइग्रेट नहीं किया गया है; BackgroundWorker दोनों पर काम करता है।
dcarl661

जवाबों:


74

async / wait का निर्माण इस तरह के निर्माण को बदलने के लिए किया गया है BackgroundWorker। यदि आप निश्चित रूप से इसका उपयोग कर सकते हैं यदि आप चाहते हैं, तो आपको कुछ अन्य टीपीएल टूल के साथ एसिंक्स / वेट का उपयोग करने में सक्षम होना चाहिए, ताकि वहां होने वाली हर चीज को संभाल सकें।

चूंकि दोनों काम करते हैं, इसलिए यह व्यक्तिगत प्राथमिकता के रूप में आता है, जिसका उपयोग आप कब करते हैं। आपके लिए जल्दी क्या है ? आपके लिए समझना आसान क्या है ?


17
धन्यवाद। मेरे लिए async / प्रतीक्षा अधिक स्पष्ट और 'स्वाभाविक' लगती है। BakcgoundWorker मेरी राय में 'शोर' कोड बनाता है।
टॉम

12
@ अच्छा, इसीलिए Microsoft ने इसे लागू करने में बहुत समय और प्रयास लगाया। अगर यह बेहतर नहीं होता तो वे परेशान नहीं होते
20

5
हाँ। नया प्रतीक्षित सामान पुराने BackgroundWorker को पूरी तरह से हीन और अप्रचलित बनाता है। अंतर यह है कि नाटकीय।
usr

16
मैंने अपने ब्लॉग पर पृष्ठभूमि कार्यों के लिए अलग-अलग तरीकों की तुलना में एक बहुत अच्छा ठहरनेवाला मिल गया है। ध्यान दें कि async/ awaitभी थ्रेड पूल थ्रेड्स के बिना अतुल्यकालिक प्रोग्रामिंग की अनुमति देता है ।
स्टीफन क्लीयर

8
इस उत्तर को अस्वीकार कर दिया, यह भ्रामक है। Async / प्रतीक्षा को पृष्ठभूमि कार्यकर्ता को बदलने के लिए डिज़ाइन नहीं किया गया है।
क्वांगो

206

यह संभवत: टीएल है, कई के लिए डॉ, लेकिन, मैं की तुलना लगता है awaitके साथ BackgroundWorkerइस का पालन पर सेब और संतरे और मेरे विचारों की तुलना की तरह है:

BackgroundWorkerकिसी एक कार्य को करने के लिए है, जिसे आप थ्रेड पूल थ्रेड पर पृष्ठभूमि में प्रदर्शन करना चाहते हैं। async/ awaitअतुल्यकालिक संचालन पर अतुल्यकालिक प्रतीक्षा के लिए एक वाक्यविन्यास है। वे ऑपरेशन थ्रेड पूल थ्रेड का उपयोग कर सकते हैं या नहीं कर सकते हैं या किसी अन्य थ्रेड का उपयोग भी कर सकते हैं । तो, वे सेब और संतरे हैं।

उदाहरण के लिए, आप निम्न के साथ कुछ कर सकते हैं await:

using (WebResponse response = await webReq.GetResponseAsync())
{
    using (Stream responseStream = response.GetResponseStream())
    {
        int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
    }
}

लेकिन, आप संभवतः कभी ऐसा नहीं करेंगे कि पृष्ठभूमि कार्यकर्ता में, आप संभवतः .NET 4.0 (पूर्व await) में ऐसा कुछ करेंगे।

webReq.BeginGetResponse(ar =>
{
    WebResponse response = webReq.EndGetResponse(ar);
    Stream responseStream = response.GetResponseStream();
    responseStream.BeginRead(buffer, 0, buffer.Length, ar2 =>
    {
        int bytesRead = responseStream.EndRead(ar2);
        responseStream.Dispose();
        ((IDisposable) response).Dispose();
    }, null);
}, null);

सूचना दोनों वाक्यविन्यास और कैसे आप उपयोग नहीं कर सकते के बीच तुलना में निपटान के disjointness usingबिना async/ await

लेकिन, आप ऐसा कुछ नहीं करेंगे BackgroundWorkerBackgroundWorkerआमतौर पर एक लंबे समय तक चलने वाले ऑपरेशन को मॉडलिंग करने के लिए है जिसे आप यूआई जवाबदेही को प्रभावित नहीं करना चाहते हैं। उदाहरण के लिए:

worker.DoWork += (sender, e) =>
                    {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                        ++i;
                    };
worker.RunWorkerCompleted += (sender, eventArgs) =>
                                {
                                    // TODO: do something on the UI thread, like
                                    // update status or display "result"
                                };
worker.RunWorkerAsync();

वहाँ वास्तव में कुछ भी नहीं है जिसका आप उपयोग कर सकते हैं async / प्रतीक्षा के साथ, BackgroundWorkerआपके लिए थ्रेड बना रहा है।

अब, आप इसके बजाय TPL का उपयोग कर सकते हैं:

var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
                      {
                        int i = 0;
                        // simulate lengthy operation
                        Stopwatch sw = Stopwatch.StartNew();
                        while (sw.Elapsed.TotalSeconds < 1)
                            ++i;
                      }).ContinueWith(t=>
                                      {
                                        // TODO: do something on the UI thread, like
                                        // update status or display "result"
                                      }, synchronizationContext);

किस स्थिति में TaskSchedulerआपके लिए थ्रेड बना रहा है (डिफ़ॉल्ट मानकर TaskScheduler), और awaitनिम्नानुसार उपयोग कर सकता है:

await Task.Factory.StartNew(() =>
                  {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                        ++i;
                  });
// TODO: do something on the UI thread, like
// update status or display "result"

मेरी राय में, एक बड़ी तुलना यह है कि आप प्रगति की रिपोर्ट कर रहे हैं या नहीं। उदाहरण के लिए, आपके पास BackgroundWorker likeयह हो सकता है :

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (sender, eventArgs) =>
                            {
                            // TODO: something with progress, like update progress bar

                            };
worker.DoWork += (sender, e) =>
                 {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                    {
                        if ((sw.Elapsed.TotalMilliseconds%100) == 0)
                            ((BackgroundWorker)sender).ReportProgress((int) (1000 / sw.ElapsedMilliseconds));
                        ++i;
                    }
                 };
worker.RunWorkerCompleted += (sender, eventArgs) =>
                                {
                                    // do something on the UI thread, like
                                    // update status or display "result"
                                };
worker.RunWorkerAsync();

लेकिन, आप इनमें से कुछ के साथ सौदा नहीं करेंगे क्योंकि आप बैकग्राउंड वर्कर कंपोनेंट को फॉर्म की डिज़ाइन सतह पर ड्रैग-एंड-ड्रॉप कर देंगे - ऐसा कुछ जो आप async/ awaitऔर साथ नहीं कर सकते Task... यानी आपने जीता ' t मैन्युअल रूप से ऑब्जेक्ट बनाएँ, गुण सेट करें और ईवेंट हैंडलर सेट करें। आप केवल के शरीर में भर देंगे DoWork, RunWorkerCompletedऔर ProgressChangedईवेंट हैंडलर्स।

यदि आपने "परिवर्तित" कर दिया है तो async / प्रतीक्षा में, आप कुछ ऐसा करेंगे:

     IProgress<int> progress = new Progress<int>();

     progress.ProgressChanged += ( s, e ) =>
        {
           // TODO: do something with e.ProgressPercentage
           // like update progress bar
        };

     await Task.Factory.StartNew(() =>
                  {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                    {
                        if ((sw.Elapsed.TotalMilliseconds%100) == 0)
                        {
                            progress.Report((int) (1000 / sw.ElapsedMilliseconds))
                        }
                        ++i;
                    }
                  });
// TODO: do something on the UI thread, like
// update status or display "result"

एक डिजाइनर सतह पर एक घटक को खींचने की क्षमता के बिना, यह वास्तव में पाठक पर निर्भर है कि वह "बेहतर" है। लेकिन, कि, मेरे लिए, के बीच तुलना है awaitऔर BackgroundWorker, नहीं है कि क्या आप में निर्मित तरीकों की तरह इंतजार कर सकते हैं Stream.ReadAsync। उदाहरण के लिए यदि आप BackgroundWorkerइच्छानुसार उपयोग कर रहे थे , तो उपयोग करने के लिए परिवर्तित करना कठिन हो सकता है await

अन्य विचार: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html


2
मुझे लगता है कि async / इंतजार के साथ एक दोष यह है कि आप एक ही बार में कई async कार्य प्रारंभ करना चाहते हैं। प्रतीक्षा का मतलब अगले कार्य को शुरू करने से पहले प्रत्येक कार्य के पूरा होने की प्रतीक्षा करना है। और यदि आप प्रतीक्षित खोजशब्द को छोड़ देते हैं तो विधि समकालिक रूप से चलती है जो आप नहीं चाहते हैं। मुझे नहीं लगता कि async / इंतजार एक समस्या को हल कर सकता है जैसे "इन 5 कार्यों को शुरू करें और मुझे वापस बुलाएं जब प्रत्येक कार्य किसी विशेष कार्य में नहीं किया जाता है"।
ट्रेवर इलियट

4
@Moozhe। सच नहीं है, आप कर सकते हैं var t1 = webReq.GetResponseAsync(); var t2 = webReq2.GetResponseAsync(); await t1; await t2;। जो दो समानांतर संचालन की प्रतीक्षा करेगा। Await अतुल्यकालिक, लेकिन अनुक्रमिक कार्यों के लिए बेहतर है, IMO ...
पीटर रिची

2
@Moozhe हाँ, इस तरह से करना एक निश्चित अनुक्रम को बनाए रखता है - जैसा कि मैंने उल्लेख किया है। यह प्रतीक्षा का मुख्य बिंदु है अनुक्रमिक-दिखने वाले कोड में एसिंक्रोनसिटी प्राप्त करना। आप निश्चित रूप से, await Task.WhenAny(t1, t2)कुछ करने के लिए उपयोग कर सकते हैं जब दोनों में से कोई भी कार्य पहले पूरा हो गया हो। आप संभवतः एक लूप चाहते हैं ताकि यह सुनिश्चित हो सके कि अन्य कार्य भी पूरा हो जाए। आमतौर पर आप जानना चाहते हैं कि कोई विशिष्ट कार्य कब पूरा होता है, जो आपको अनुक्रमिक लेखन की ओर ले जाता है await
पीटर रिची


5
ईमानदारी, बैकग्राउंडवॉकर आईओ-बाउंड ऑपरेशन के लिए कभी भी अच्छा नहीं था ।
पीटर रिची

21

यह एक अच्छा परिचय है: http://msdn.microsoft.com/en-us/library/hh191443.aspx थ्रेड्स सेक्शन वह है जो आप ढूंढ रहे हैं:

Async विधियों का उद्देश्य गैर-ब्लॉकिंग ऑपरेशन होना है। एक async विधि में एक प्रतीक्षित अभिव्यक्ति वर्तमान थ्रेड को ब्लॉक नहीं करती है जबकि प्रतीक्षित कार्य चल रहा है। इसके बजाय, अभिव्यक्ति शेष विधि को एक निरंतरता के रूप में संकेत देती है और एस्किंस विधि के कॉलर को नियंत्रण वापस करती है।

Async और प्रतीक्षा कीवर्ड के कारण अतिरिक्त थ्रेड्स का निर्माण नहीं होता है। Async विधियों को मल्टीथ्रेडिंग की आवश्यकता नहीं है क्योंकि एक async विधि अपने स्वयं के थ्रेड पर नहीं चलती है। विधि वर्तमान सिंक्रनाइज़ेशन संदर्भ पर चलती है और थ्रेड पर समय का उपयोग तब ही करती है जब विधि सक्रिय होती है। CPU- बाउंड वर्क को बैकग्राउंड थ्रेड में ले जाने के लिए आप Task.Run का उपयोग कर सकते हैं, लेकिन एक बैकग्राउंड थ्रेड ऐसी प्रक्रिया से मदद नहीं करता है, जो परिणाम उपलब्ध होने की प्रतीक्षा कर रहा है।

अतुल्यकालिक प्रोग्रामिंग के लिए एसिंक्स-आधारित दृष्टिकोण लगभग हर मामले में मौजूदा दृष्टिकोणों के लिए बेहतर है। विशेष रूप से, यह दृष्टिकोण IO- बाउंड ऑपरेशंस के लिए BackgroundWorker से बेहतर है क्योंकि कोड सरल है और आपको दौड़ की स्थिति की रक्षा करने की आवश्यकता नहीं है। Task.Run के संयोजन में, async प्रोग्रामिंग CPU-बाउंड ऑपरेशंस के लिए BackgroundWorker से बेहतर है क्योंकि Async प्रोग्रामिंग आपके कोड को उस कार्य से चलाने के समन्वय विवरण को अलग करती है जो Task.Run थ्रेडपूल में स्थानांतरित करता है।


"IO- बाउंड ऑपरेशंस के लिए क्योंकि कोड सरल है और आपको रेस की स्थितियों से बचाव करने की ज़रूरत नहीं है" क्या रेस की स्थितियाँ उत्पन्न हो सकती हैं, क्या आप एक उदाहरण दे सकते हैं?
इरान ओटज़प

8

BackgroundWorker को स्पष्ट रूप से .NET 4.5 में अप्रचलित के रूप में लेबल किया गया है:

MSDN लेख "Async और Await (C # और विजुअल बेसिक) के साथ अतुल्यकालिक प्रोग्रामिंग" बताता है:

अतुल्यकालिक प्रोग्रामिंग के लिए एसिंक्स-आधारित दृष्टिकोण लगभग हर मामले में मौजूदा दृष्टिकोणों के लिए बेहतर है । विशेष रूप से, यह दृष्टिकोण IO- बाउंड संचालन के लिए BackgroundWorker की तुलना में बेहतर है क्योंकि कोड सरल है और आपको दौड़ की स्थिति की रक्षा करने की आवश्यकता नहीं है। Task.Run के संयोजन में, async प्रोग्रामिंग CPU- बाउंड ऑपरेशंस के लिए BackgroundWorker से बेहतर है क्योंकि Async प्रोग्रामिंग उस कार्य से आपके कोड को चलाने के समन्वय विवरण को अलग करती है जो Task.Run थ्रेडपूल में स्थानांतरित करता है

अपडेट करें

  • के जवाब में @ eran-otzap की टिप्पणी:
    "आईओ बाध्य कार्यों के लिए क्योंकि कोड सरल है और आप दौड़ की स्थिति से बचने के लिए की जरूरत नहीं है" क्या दौड़ की स्थिति occure कर सकते हैं, आपको एक उदाहरण देता सकता है? "

इस सवाल को एक अलग पोस्ट के रूप में रखा जाना चाहिए था।

विकिपीडिया में रेसिंग की स्थिति की अच्छी व्याख्या है । इसका आवश्यक हिस्सा मल्टीथ्रेडिंग है और एक ही MSDN आलेख Async और Await (C # और Visual Basic) के साथ अतुल्यकालिक प्रोग्रामिंग में :

Async विधियों का उद्देश्य गैर-ब्लॉकिंग ऑपरेशन होना है। एक async विधि में एक प्रतीक्षित अभिव्यक्ति वर्तमान थ्रेड को ब्लॉक नहीं करती है जबकि प्रतीक्षित कार्य चल रहा है। इसके बजाय, अभिव्यक्ति शेष विधि को एक निरंतरता के रूप में संकेत देती है और एस्किंस विधि के कॉलर को नियंत्रण वापस करती है।

Async और प्रतीक्षा कीवर्ड के कारण अतिरिक्त थ्रेड्स का निर्माण नहीं होता है। Async विधियों को मल्टीथ्रेडिंग की आवश्यकता नहीं है क्योंकि एक async विधि अपने स्वयं के थ्रेड पर नहीं चलती है। विधि वर्तमान सिंक्रनाइज़ेशन संदर्भ पर चलती है और थ्रेड पर समय का उपयोग तब ही करती है जब विधि सक्रिय होती है। CPU- बाउंड वर्क को बैकग्राउंड थ्रेड में ले जाने के लिए आप Task.Run का उपयोग कर सकते हैं, लेकिन एक बैकग्राउंड थ्रेड ऐसी प्रक्रिया से मदद नहीं करता है, जो परिणाम उपलब्ध होने की प्रतीक्षा कर रहा है।

अतुल्यकालिक प्रोग्रामिंग के लिए एसिंक्स-आधारित दृष्टिकोण लगभग हर मामले में मौजूदा दृष्टिकोणों के लिए बेहतर है। विशेष रूप से, यह दृष्टिकोण IO- बाउंड ऑपरेशंस के लिए BackgroundWorker से बेहतर है क्योंकि कोड सरल है और आपको दौड़ की स्थिति की रक्षा करने की आवश्यकता नहीं है। Task.Run के संयोजन में, async प्रोग्रामिंग CPU- बाउंड ऑपरेशंस के लिए BackgroundWorker से बेहतर है क्योंकि Async प्रोग्रामिंग उस कार्य से आपके कोड को चलाने के समन्वय विवरण को अलग करती है जो Task.Run थ्रेडपूल में स्थानांतरित करता है

यही है, "इस async और प्रतीक्षारत कीवर्ड अतिरिक्त थ्रेड बनाने के लिए कारण नहीं है"।

जहां तक ​​मैं अपने स्वयं के प्रयासों को याद कर सकता हूं, जब मैं एक साल पहले इस लेख का अध्ययन कर रहा था, यदि आप एक ही लेख से कोड नमूने के साथ चले और खेले हैं, तो आप इस स्थिति में टकरा सकते हैं कि इसके गैर-एसिक्स संस्करण (आप परिवर्तित करने का प्रयास कर सकते हैं) अपने आप से) अनिश्चित काल के लिए ब्लॉक करें!

इसके अलावा, ठोस उदाहरणों के लिए आप इस साइट को खोज सकते हैं। यहाँ कुछ उदाहरण हैं:


30
BackgrondWorker को स्पष्ट रूप से .NET 4.5 में अप्रचलित के रूप में लेबल नहीं किया गया है। MSDN आलेख केवल यह कहता है कि IO- बाउंड ऑपरेशन async विधियों के साथ बेहतर हैं - BackgroundWorker का उपयोग करने का मतलब यह नहीं है कि आप Inync विधियों का उपयोग नहीं कर सकते हैं।
पीटर रिची

@PeterRitchie, मैंने अपना उत्तर सही किया। मेरे लिए, "मौजूदा दृष्टिकोण अप्रचलित हैं" का पर्यायवाची है "अतुल्यकालिक प्रोग्रामिंग के लिए एसिंक्स-आधारित दृष्टिकोण लगभग हर मामले में मौजूदा दृष्टिकोणों के लिए बेहतर है"
Gennady Vanin Геннадий Ванин

7
मैं उस MSDN पृष्ठ के साथ समस्या लेता हूं। एक के लिए, आप टास्क के साथ बीजीडब्ल्यू के साथ कोई "समन्वय" नहीं करते हैं। और, हाँ बीजीडब्ल्यू को सीधे आईओ ऑपरेशन करने का इरादा नहीं था - बीजीडब्ल्यू की तुलना में आईओ करने के लिए हमेशा एक बेहतर तरीका होगा। अन्य जवाब से पता चलता है कि बीजीडब्ल्यू टास्क की तुलना में अधिक जटिल नहीं है। और यदि आप BGW का सही उपयोग करते हैं, तो कोई दौड़ की स्थिति नहीं है।
पीटर रिची

"IO- बाउंड ऑपरेशंस के लिए क्योंकि कोड सरल है और आपको रेस की स्थितियों से बचाव करने की ज़रूरत नहीं है" क्या रेस की स्थितियाँ उत्पन्न हो सकती हैं, क्या आप एक उदाहरण दे सकते हैं?
इरान ओटज़ैप

11
यह उत्तर गलत है। अतुल्यकालिक प्रोग्रामिंग भी गैर-तुच्छ कार्यक्रमों में गतिरोधों को आसानी से ट्रिगर कर सकती है। तुलना में BackgroundWorker सरल और रॉक सॉलिड है।
ZunTzu
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.