BackgroundWorker vs background Thread

166

Saya punya pertanyaan gaya tentang pilihan implementasi latar belakang thread yang harus saya gunakan pada aplikasi windows form. Saat ini saya memiliki BackgroundWorkerformulir yang memiliki infinite (while(true))loop. Dalam loop ini saya gunakan WaitHandle.WaitAnyuntuk menjaga agar thread tetap tertunda sampai sesuatu yang menarik terjadi. Salah satu pegangan acara yang saya tunggu adalah acara " StopThread" sehingga saya bisa keluar dari loop. Peristiwa ini ditandai ketika saya ditimpa Form.Dispose().

Saya membaca suatu tempat yang BackgroundWorkerbenar-benar ditujukan untuk operasi yang Anda tidak ingin mengikat UI dengan dan memiliki akhir yang terbatas - seperti mengunduh file, atau memproses urutan item. Dalam hal ini "akhir" tidak diketahui dan hanya ketika jendela ditutup. Oleh karena itu apakah lebih tepat bagi saya untuk menggunakan Thread latar belakang daripada BackgroundWorkeruntuk tujuan ini?

freddy smith
sumber

Jawaban:

88

Dari pemahaman saya tentang pertanyaan Anda, Anda menggunakan BackgroundWorkersebagai Thread standar.

Alasan mengapa BackgroundWorkerdisarankan untuk hal-hal yang Anda tidak ingin mengikat utas UI adalah karena hal itu memaparkan beberapa peristiwa yang menyenangkan ketika melakukan pengembangan Formulir Win.

Acara suka RunWorkerCompletedmemberi sinyal ketika utas telah menyelesaikan apa yang perlu dilakukan, dan ProgressChangedacara untuk memperbarui GUI pada utas berlangsung.

Jadi jika Anda tidak menggunakan ini, saya tidak melihat ada salahnya menggunakan Thread standar untuk apa yang perlu Anda lakukan.

ParmesanCodice
sumber
satu masalah lain yang saya tidak yakin adalah, misalkan saya mencoba untuk membuang formulir yang memiliki pekerja latar belakang berjalan di atasnya. Saya memberi sinyal shutdownevent (ManualResetEvent) dan beberapa saat kemudian DoWork akan keluar dengan anggun. Haruskah saya membiarkan formulir melanjutkan dan Buang meskipun DoWork mungkin membutuhkan waktu sedikit lebih lama untuk diselesaikan, atau apakah ada cara (dan apakah itu lebih baik) untuk di-thread.Gabung dengan pekerja latar belakang sampai benar-benar keluar dan kemudian biarkan Buang bentuk terus?
freddy smith
Saya pikir BackgroundWorker. IsBusy adalah apa yang Anda cari di sana.
ParmesanCodice
1
Gunakan hanya CancelAsync(dan uji CancellationPendingapakah thread Anda akan polling pada interval pendek, jika Anda ingin memiliki pengecualian yang diangkat, gunakan System.Threading.Thread.Abort()yang meningkatkan pengecualian di dalam blok thread itu sendiri, pilih model yang tepat untuk situasi ini.
Brett Ryan
369

Beberapa pemikiran saya ...

  1. Gunakan BackgroundWorker jika Anda memiliki satu tugas yang berjalan di latar belakang dan perlu berinteraksi dengan UI. Tugas untuk membuat panggilan data dan metode ke utas UI ditangani secara otomatis melalui model berbasis peristiwa. Hindari BackgroundWorker jika ...
    • majelis Anda tidak memiliki atau tidak berinteraksi langsung dengan UI,
    • Anda perlu utas untuk menjadi utas depan, atau
    • Anda perlu memanipulasi prioritas utas.
  2. Gunakan utas ThreadPool saat efisiensi diinginkan. ThreadPool membantu menghindari overhead yang terkait dengan membuat, memulai, dan menghentikan utas. Hindari menggunakan ThreadPool jika ...
    • tugas berjalan untuk aplikasi Anda seumur hidup,
    • Anda perlu utas untuk menjadi utas depan,
    • Anda perlu memanipulasi utas utas, atau
    • Anda memerlukan utas untuk memiliki identitas tetap (batal, ditangguhkan, ditemukan).
  3. Gunakan kelas Thread untuk tugas-tugas jangka panjang dan ketika Anda membutuhkan fitur yang ditawarkan oleh model threading formal, misalnya, memilih antara thread foreground dan background, mengutak-atik prioritas utas, kontrol berbutir halus atas eksekusi thread, dll.
Matt Davis
sumber
10
Pekerja latar belakang dalam perakitan System.dll dan System.ComponentModel namespace. Tidak ada ketergantungan pada WinForms.
Kugel
17
Itu benar, tetapi BackgroundWorkerdirancang untuk melaporkan kemajuan utas ke pihak yang berkepentingan, yang biasanya melibatkan UI. Dokumentasi MSDN untuk kelas membuatnya sangat jelas. Jika Anda hanya perlu melakukan tugas di latar belakang, lebih baik menggunakan ThreadPoolutas.
Matt Davis
5
Berkenaan dengan poin Anda tentang System.Windows.Formsmajelis; BackgroundWorkerjuga berguna untuk aplikasi WPF dan aplikasi tersebut mungkin tidak memiliki referensi ke WinForms.
GiddyUpHorsey
12

Cukup banyak apa yang dikatakan Matt Davis, dengan poin tambahan berikut:

Bagi saya pembeda utama dengan BackgroundWorkeradalah marshalling otomatis dari acara selesai melalui SynchronizationContext. Dalam konteks UI, ini berarti acara yang selesai diaktifkan pada utas UI, dan karenanya dapat digunakan untuk memperbarui UI. Ini adalah pembeda utama jika Anda menggunakan BackgroundWorkerdalam konteks UI.

Tugas yang dieksekusi melalui the ThreadPooltidak dapat dengan mudah dibatalkan (ini termasuk ThreadPool. QueueUserWorkItemDan delegasi mengeksekusi secara asinkron). Jadi sementara itu menghindari overhead dari perputaran thread, jika Anda perlu pembatalan baik menggunakan BackgroundWorkeratau (lebih mungkin di luar UI) putar sebuah thread dan simpan referensi untuk itu sehingga Anda dapat menelepon Abort().

dermaga7
sumber
1
Hanya ... semoga aplikasi ini dirancang tentang metode bersih untuk menghentikan tugas berulir (Abort biasanya bukan)
11

Anda juga mengikat utas threadpool untuk seumur hidup pekerja latar belakang, yang mungkin menjadi perhatian karena hanya ada sejumlah terbatas dari mereka. Saya akan mengatakan bahwa jika Anda hanya pernah membuat utas sekali untuk aplikasi Anda (dan tidak menggunakan salah satu fitur pekerja latar belakang) maka gunakan utas, bukan utas pengrajin latar / threadpool.

Mat
sumber
1
Saya pikir ini adalah poin yang bagus. Jadi pesan yang saya ambil dari ini adalah menggunakan pekerja latar belakang jika Anda membutuhkan utas latar belakang "sementara" selama masa pakai Formulir, namun jika Anda memerlukan utas latar belakang untuk seluruh masa pakai formulir (yang bisa berupa menit, jam, hari ...) kemudian gunakan Thread alih-alih BackgroundWorker agar tidak menyalahgunakan tujuan ThreadPool
freddy smith
Mengenai: "... yang mungkin menjadi perhatian karena hanya ada sejumlah terbatas", apakah maksud Anda aplikasi lain di OS mungkin membutuhkannya dan berbagi dari 'kumpulan' yang sama?
Dan W
8

Anda tahu, kadang-kadang lebih mudah untuk bekerja dengan BackgroundWorker terlepas dari apakah Anda menggunakan Windows Forms, WPF atau teknologi apa pun. Bagian yang rapi tentang orang-orang ini adalah Anda mendapatkan threading tanpa harus terlalu khawatir tentang di mana Anda menjalankan thread, yang bagus untuk tugas-tugas sederhana.

Sebelum menggunakan BackgroundWorkerpertimbangan terlebih dahulu jika Anda ingin membatalkan utas (aplikasi penutupan, pembatalan pengguna) maka Anda harus memutuskan apakah utas Anda harus memeriksa pembatalan atau apakah harus diarahkan pada eksekusi itu sendiri.

BackgroundWorker.CancelAsync()akan diatur CancellationPendingke truetetapi tidak akan melakukan apa-apa lagi, tanggung jawab utas untuk terus memeriksa ini, perlu diingat juga bahwa Anda bisa berakhir dengan kondisi balapan dalam pendekatan ini di mana pengguna Anda dibatalkan, tetapi utas selesai sebelum pengujian untuk CancellationPending.

Thread.Abort() di sisi lain akan melempar pengecualian dalam eksekusi utas yang memberlakukan pembatalan utas itu, Anda harus berhati-hati tentang apa yang mungkin berbahaya jika pengecualian ini tiba-tiba muncul dalam eksekusi sekalipun.

Threading perlu pertimbangan yang sangat hati-hati apa pun tugasnya, untuk bacaan lebih lanjut:

Pemrograman Paralel dalam .NET Framework Managed Threading Best Practices

Brett Ryan
sumber
5

Saya tahu cara menggunakan utas sebelum saya tahu. NET, jadi butuh waktu untuk membiasakan diri ketika saya mulai menggunakan BackgroundWorkers. Matt Davis telah merangkum perbedaannya dengan keunggulan luar biasa, tetapi saya ingin menambahkan bahwa lebih sulit untuk memahami dengan tepat apa yang dilakukan oleh kode, dan ini dapat membuat proses debug lebih sulit. Lebih mudah untuk berpikir tentang membuat dan mematikan utas, IMO, daripada memikirkan memberi pekerjaan pada kumpulan utas.

Saya masih belum bisa mengomentari pos orang lain, jadi maafkan ketimpangan sesaat saya dalam menggunakan jawaban untuk mengatasi piers7

Jangan gunakan Thread.Abort();, beri tanda suatu acara dan rancang utas Anda untuk berakhir dengan anggun saat diisyaratkan. Thread.Abort()memunculkan ThreadAbortExceptionpada titik sewenang-wenang dalam eksekusi utas, yang dapat melakukan semua jenis hal-hal yang tidak bahagia seperti Monitor yatim, negara bersama yang korup, dan sebagainya.
http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx

ajs410
sumber
2

Jika tidak rusak - perbaiki sampai ... hanya bercanda :)

Tapi serius BackgroundWorker mungkin sangat mirip dengan apa yang sudah Anda miliki, seandainya Anda memulainya dari awal mungkin Anda akan menghemat waktu - tetapi pada titik ini saya tidak melihat kebutuhan. Kecuali ada sesuatu yang tidak berfungsi, atau Anda pikir kode Anda saat ini sulit dimengerti, maka saya akan tetap dengan apa yang Anda miliki.

Gandalf
sumber
2

Perbedaan dasarnya adalah, seperti yang Anda nyatakan, menghasilkan peristiwa GUI dari BackgroundWorker. Jika utas tidak perlu memperbarui tampilan atau membuat acara untuk utas GUI utama, maka utas tersebut dapat sederhana.

David R Tribble
sumber
2

Saya ingin menunjukkan satu perilaku kelas BackgroundWorker yang belum disebutkan. Anda bisa membuat Thread normal untuk berjalan di latar belakang dengan mengatur properti Thread.IsBackground.

Utas latar belakang identik dengan utas latar depan, kecuali utas latar belakang tidak mencegah proses pengakhiran. [ 1 ]

Anda bisa menguji perilaku ini dengan memanggil metode berikut di konstruktor jendela formulir Anda.

void TestBackgroundThread()
{
    var thread = new Thread((ThreadStart)delegate()
    {
        long count = 0;
        while (true)
        {
            count++;
            Debug.WriteLine("Thread loop count: " + count);
        }
    });

    // Choose one option:
    thread.IsBackground = true; // <--- This will make the thread run in background
    thread.IsBackground = false; // <--- This will delay program termination

    thread.Start();
}

Ketika properti IsBackground disetel ke true dan Anda menutup jendela, maka aplikasi Anda akan berakhir secara normal.

Tetapi ketika properti IsBackground diatur ke false (secara default) dan Anda menutup jendela, maka hanya jendela yang akan menghilang tetapi prosesnya tetap berjalan.

Kelas BackgroundWorker memanfaatkan Thread yang berjalan di latar belakang.

Doomjunky
sumber
1

Pekerja latar belakang adalah kelas yang bekerja di utas terpisah, tetapi menyediakan fungsionalitas tambahan yang tidak Anda dapatkan dengan Utas sederhana (seperti penanganan laporan progres tugas).

Jika Anda tidak memerlukan fitur tambahan yang diberikan oleh pekerja latar belakang - dan sepertinya Anda tidak - maka Thread akan lebih sesuai.

Cesar
sumber
-1

Yang membingungkan bagi saya adalah bahwa desainer studio visual hanya memungkinkan Anda untuk menggunakan BackgroundWorkers dan Timers yang tidak benar-benar bekerja dengan proyek layanan.

Ini memberi Anda kontrol seret dan lepas yang rapi ke layanan Anda, tetapi ... bahkan tidak mencoba menggunakannya. Tidak akan bekerja

Layanan: Hanya gunakan System.Timers.Timer System.Windows.Forms.Timer tidak akan berfungsi meskipun tersedia di kotak alat

Layanan: BackgroundWorkers tidak akan berfungsi saat berjalan sebagai layanan Gunakan System.Threading.ThreadPools sebagai gantinya atau panggilan Async

scottweeden
sumber