Async / bekle vs BackgroundWorker


162

Son birkaç gün içinde .net 4.5 ve c # 5'in yeni özelliklerini test ettim.

Yeni async / bekliyor özelliklerini seviyorum. Daha önce duyarlı UI ile arka planda daha uzun süreçleri işlemek için BackgroundWorker kullanmıştım .

Benim sorum: Bu güzel yeni özelliklere sahip olduktan sonra, ne zaman async / await ve ne zaman bir BackgroundWorker kullanmalıyım ? Her ikisi için ortak senaryolar hangileridir?



Her ikisi de iyidir, ancak daha sonraki bir .net sürümüne geçirilmemiş eski kodlarla çalışıyorsanız; BackgroundWorker her ikisinde de çalışır.
dcarl661

Yanıtlar:


74

async / await gibi yapıların yerini alacak şekilde tasarlanmıştır BackgroundWorker. Kesinlikle iken edebilir isterseniz kullanabilirsiniz, orada dışarıda kolu her şeye, diğer birkaç TPL araçlarla birlikte bekliyoruz / zaman uyumsuz kullanmak gerekir.

Her ikisi de çalıştığından beri, ne zaman kullandığınız kişisel tercihinize bağlıdır. İçin daha hızlı nedir senin ? Ne için daha kolaydır sen anlamak için?


17
Teşekkürler. Benim için async / bekliyor çok daha net ve 'doğal' görünüyor. Bence BakcgoundWorker kodu 'gürültülü' yapıyor.
Tom

12
@Tom Microsoft bu yüzden onu uygulamak için çok zaman ve çaba harcadı . Daha iyi olmasaydı rahatsız
olmazlardı

5
Evet. Yeni beklenen şeyler, eski BackgroundWorker'ın tamamen aşağı ve modası geçmiş görünmesini sağlıyor. Aradaki fark dramatik.
usr

16
Blogumda arka plan görevlerine farklı yaklaşımları karşılaştırarak oldukça iyi bir özetim var . async/ / awaitAynı zamanda iş parçacığı havuzu iş parçacıkları olmadan eşzamansız programlamaya izin verdiğini unutmayın .
Stephen Cleary

8
Bu cevabı reddetti, yanıltıcı. Async / await, arka plan çalışanının yerini almak üzere tasarlanmamıştır.
Quango

206

Ben karşılaştırarak düşünüyorum birçoğu için DR ancak; Bu büyük olasılıkla TL awaitile BackgroundWorker: Bu takip üzerine elma ve portakal ve düşüncelerimi karşılaştırmaya benzer

BackgroundWorker arka planda gerçekleştirmek istediğiniz tek bir görevi bir iş parçacığı havuzu iş parçacığında modellemek içindir. async/ await, eşzamansız olarak eşzamansız işlemleri bekleyen bir sözdizimidir. Bu işlemler bir iş parçacığı havuzu iş parçacığı kullanabilir veya kullanmayabilir veya başka bir iş parçacığı kullanabilir . Yani, bunlar elma ve portakal.

Örneğin, aşağıdakine benzer bir şey yapabilirsiniz: await :

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

Ancak, büyük olasılıkla bir arka plan çalışanında muhtemelen .NET 4.0'da (öncesinde await) böyle bir şey yapacağınızı modellemezsiniz :

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);

İki sözdizimi ve / usingolmadan nasıl kullanamayacağınızla karşılaştırıldığında, elden çıkarmanın ayrıklığına dikkat edin .asyncawait

Ama böyle bir şey yapmazdın BackgroundWorker . BackgroundWorkergenellikle kullanıcı arayüzü yanıt hızını etkilemek istemediğiniz tek bir uzun süreli işlemi modellemek içindir. Örneğin:

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 / await ile kullanabileceğiniz gerçekten hiçbir şey yok, sizin BackgroundWorkeriçin iş parçacığı oluşturuyor.

Artık bunun yerine TPL'yi kullanabilirsiniz:

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);

Bu durumda, TaskScheduleriş parçacığını sizin için oluşturuyor (varsayılanı varsayarak TaskScheduler) ve awaitaşağıdaki gibi kullanabilirsiniz :

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"

Kanımca, büyük bir karşılaştırma ilerlemeyi bildirip bildirmediğinizdir. Örneğin, aşağıdakilere sahip olabilirsiniz 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();

Ancak, bunlardan bazılarıyla uğraşmazsınız çünkü arka plan çalışan bileşenini bir formun tasarım yüzeyine sürükleyip bırakacaksınız - async/ ile yapamayacağınız bir şey awaitve Task... yani kazandınız ' t Nesneyi el ile oluşturma, özellikleri ayarlama ve olay işleyicileri ayarlama. sadece bedenini doldurursun DoWork,RunWorkerCompleted ve ProgressChangedolay işleyicileri.

Bunu eşzamansız / beklemeye "dönüştürdüyseniz" şöyle bir şey yaparsınız:

     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"

Bir bileşeni Tasarımcı yüzeyine sürükleme yeteneği olmadan hangisinin "daha iyi" olduğuna karar vermek gerçekten okuyucuya bağlıdır. Ama, bu, bana göre, aralarında karşılaştırmadır awaitve BackgroundWorkerdeğil sizi bekliyor yerleşik olabilir gibi yöntemlerle ister Stream.ReadAsync. örneğin,BackgroundWorker , amaçlandığı gibi , dönüştürmek zor olabilir await.

Diğer düşünceler: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html


2
Async / await ile var olduğunu düşündüğüm bir kusur, aynı anda birden fazla asenkron görevi başlatmak isteyebileceğinizdir. await, bir sonrakine başlamadan önce her görevin tamamlanmasını beklemek içindir. Await anahtar sözcüğünü atlarsanız, yöntem eşzamanlı olarak çalışır, bu da istediğiniz şey değildir. Async / await'in "bu 5 görevi başlat ve her görev belirli bir sırada yapılmadığında beni ara" gibi bir sorunu çözebileceğini sanmıyorum.
Trevor Elliott

4
@Moozhe. Doğru değil, yapabilirsin var t1 = webReq.GetResponseAsync(); var t2 = webReq2.GetResponseAsync(); await t1; await t2;. Bu iki paralel operasyonu beklerdi. Asenkron, sıralı görevler, IMO için çok daha iyi ...
Peter Ritchie

2
@Moozhe evet, bu şekilde yaptığım belirli bir diziyi korur - bahsettiğim gibi. bu ana beklemek sıralı görünümlü kod asenkronizasyon elde etmektir. Elbette, await Task.WhenAny(t1, t2)her iki görev de ilk tamamlandığında bir şeyler yapmak için kullanabilirsiniz . Muhtemelen diğer görevin de tamamlandığından emin olmak için bir döngü istersiniz. Genellikle belirli bir görevin ne zaman tamamlandığını bilmek istersiniz , bu da sizi sıralı awaits yazmanıza yol açar .
Peter Ritchie


5
Dürüstlük, BackgroundWorker asla IO'ya bağlı operasyonlar için iyi değildi .
Peter Ritchie

21

Bu iyi bir tanıtımdır: http://msdn.microsoft.com/en-us/library/hh191443.aspx Konular bölümü tam aradığınız şeydir:

Asenkron yöntemlerin bloke olmayan işlemler olması amaçlanmıştır. Zaman uyumsuz bir yöntemde bekleyen bir ifade, beklenen görev çalışırken geçerli iş parçacığını engellemez. Bunun yerine, ifade, yöntemin geri kalanını bir devamı olarak kaydeder ve denetimi, zaman uyumsuz yöntemin arayanına döndürür.

Zaman uyumsuz ve beklenen anahtar kelimeler ek iş parçacıklarının oluşturulmasına neden olmaz. Eşzamansız yöntemler kendi iş parçacığında çalışmadığından, eşzamansız yöntemler çoklu iş parçacığı gerektirmez. Yöntem geçerli eşitleme bağlamında çalışır ve iş parçacığındaki zamanı yalnızca yöntem etkin olduğunda kullanır. CPU'ya bağlı çalışmayı bir arka plan iş parçacığına taşımak için Task.Run komutunu kullanabilirsiniz, ancak arka plan iş parçacığı, yalnızca sonuçların kullanılabilir olmasını bekleyen bir işlem için yardımcı olmaz.

Zaman uyumsuz programlamaya asenkron tabanlı yaklaşım, hemen hemen her durumda mevcut yaklaşımlara tercih edilir. Özellikle, bu yaklaşım IO-bağlantılı işlemler için BackgroundWorker'dan daha iyidir çünkü kod daha basittir ve yarış koşullarına karşı korunmanız gerekmez. Task.Run ile birlikte, zaman uyumsuz programlama CPU'ya bağlı işlemler için BackgroundWorker'dan daha iyidir çünkü async programlama, kodunuzu çalıştırmanın koordinasyon detaylarını Task.Run iş parçacığına aktardığı işten ayırır.


“IO-bağlı operasyonlar için kod daha basit olduğu ve yarış koşullarına karşı korunmanız gerekmediği için” Hangi yarış koşulları oluşabilir, bir örnek verebilir misiniz?
eran otzap

8

BackgroundWorker , .NET 4.5'te açıkça eski olarak etiketlenmiştir:

"Async ve Await (C # ve Visual Basic) ile Eşzamansız Programlama" adlı MSDN makalesi şöyle anlatıyor:

Zaman uyumsuz programlamaya asenkron tabanlı yaklaşım, hemen hemen her durumda mevcut yaklaşımlara tercih edilir . Özellikle, bu yaklaşım IO-bağlantılı işlemler için BackgroundWorker'dan daha iyidir çünkü kod daha basittir ve yarış koşullarına karşı korunmanız gerekmez. Task.Run ile birlikte, eşzamansız programlama, CPU'ya bağlı işlemler için BackgroundWorker'dan daha iyidir, çünkü eşzamansız programlama, kodunuzu çalıştırmanın koordinasyon ayrıntılarını Task.Run'un threadpool'a aktardığı işten ayırır.

GÜNCELLEME

Bu soru ayrı bir yazı olarak sorulmalıydı.

Wikipedia'nın yarış koşulları hakkında iyi bir açıklaması var . Bunun gerekli kısmı çok iş parçacıklı ve aynı MSDN makalesinden Async ve Await (C # ve Visual Basic) ile Eşzamansız Programlama :

Asenkron yöntemlerin bloke olmayan işlemler olması amaçlanmıştır. Zaman uyumsuz bir yöntemde bekleyen bir ifade, beklenen görev çalışırken geçerli iş parçacığını engellemez. Bunun yerine, ifade, yöntemin geri kalanını bir devamı olarak kaydeder ve denetimi, zaman uyumsuz yöntemin arayanına döndürür.

Zaman uyumsuz ve beklenen anahtar kelimeler ek iş parçacıklarının oluşturulmasına neden olmaz. Eşzamansız yöntemler kendi iş parçacığında çalışmadığından, eşzamansız yöntemler çoklu iş parçacığı gerektirmez. Yöntem geçerli eşitleme bağlamında çalışır ve iş parçacığındaki zamanı yalnızca yöntem etkin olduğunda kullanır. CPU'ya bağlı çalışmayı bir arka plan iş parçacığına taşımak için Task.Run komutunu kullanabilirsiniz, ancak arka plan iş parçacığı, yalnızca sonuçların kullanılabilir olmasını bekleyen bir işlem için yardımcı olmaz.

Zaman uyumsuz programlamaya asenkron tabanlı yaklaşım, hemen hemen her durumda mevcut yaklaşımlara tercih edilir. Özellikle, bu yaklaşım IO-bağlantılı işlemler için BackgroundWorker'dan daha iyidir çünkü kod daha basittir ve yarış koşullarına karşı korunmanız gerekmez. Task.Run ile birlikte, eşzamansız programlama, CPU'ya bağlı işlemler için BackgroundWorker'dan daha iyidir, çünkü eşzamansız programlama, kodunuzu çalıştırmanın koordinasyon ayrıntılarını Task.Run'un threadpool'a aktardığı işten ayırır.

Yani, "Eşzamansız ve bekleyen anahtar kelimeler ek iş parçacıklarının oluşturulmasına neden olmaz".

Bir yıl önce bu makaleyi incelerken kendi girişimlerimi hatırlayabildiğim kadarıyla, aynı makaleden kod örneği ile oynadıysanız ve oynadıysanız, zaman uyumsuz sürümlerinin (dönüştürmeyi deneyebilirsiniz) çarpabilirsin kendinize saklayın) süresiz olarak engelle!

Ayrıca, somut örnekler için bu sitede arama yapabilirsiniz. İşte bazı örnek:


30
BackgrondWorker edilir değil açıkça .NET 4.5 eskimiş olarak etiketlenmiş. MSDN makalesi, yalnızca IO'ya bağlı işlemlerin zaman uyumsuz yöntemlerle daha iyi olduğunu söylüyor - BackgroundWorker kullanımı, zaman uyumsuz yöntemleri kullanamayacağınız anlamına gelmiyor.
Peter Ritchie

@PeterRitchie, cevabımı düzelttim. Benim için "mevcut yaklaşımlar artık kullanılmıyor", "Zaman uyumsuz programlamaya yönelik eşzamansız tabanlı yaklaşım hemen hemen her durumda mevcut yaklaşımlara tercih edilir" ile eşanlamlıdır
Gennady Vanin Геннадий Ванин 4:13

7
Bu MSDN sayfasıyla ilgili sorunu alıyorum. Birincisi, BGW ile Görev ile olduğundan daha fazla "koordinasyon" yapmazsınız. Ve evet, BGW hiçbir zaman doğrudan IO operstionları gerçekleştirmek için tasarlanmamıştı - IO yapmanın her zaman BGW'den daha iyi bir yolu olurdu . Diğer cevap, BGW'nin Görev'den daha karmaşık olmadığını gösteriyor. Ve BGW'yi doğru kullanırsanız, yarış koşulları yoktur.
Peter Ritchie

“IO-bağlı operasyonlar için kod daha basit olduğu için ve yarış koşullarına karşı korunmanız gerekmiyor” Hangi yarış koşulları oluşabilir, bir örnek verebilir misiniz?
eran otzap

11
Bu cevap yanlış. Asenkron programlama, önemsiz olmayan programlarda kilitlenmeleri çok kolay tetikleyebilir. Buna karşılık BackgroundWorker basit ve kaya gibi sağlamdır.
ZunTzu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.