Adakah kasus ketika lebih baik menggunakan objek Thread lama biasa daripada salah satu konstruksi yang lebih baru?

112

Saya melihat banyak orang di posting blog dan di sini di SO menghindari atau menasihati penggunaan Threadkelas dalam versi terbaru C # (dan maksud saya tentu saja 4.0+, dengan tambahan Task& teman). Bahkan sebelumnya, ada perdebatan tentang fakta bahwa fungsi thread lama yang biasa dapat diganti dalam banyak kasus oleh ThreadPoolkelas.

Juga, mekanisme khusus lainnya selanjutnya membuat Threadkelas menjadi kurang menarik, seperti Timermengganti combo Thread+ jelek Sleep, sedangkan untuk GUI yang kami miliki BackgroundWorker, dll.

Namun, Threadtampaknya tetap menjadi konsep yang sangat akrab bagi sebagian orang (termasuk saya), orang-orang yang, ketika dihadapkan dengan tugas yang melibatkan beberapa jenis eksekusi paralel, langsung melompat menggunakan Threadkelas lama yang baik . Akhir-akhir ini saya bertanya-tanya apakah sudah waktunya untuk mengubah cara saya.

Jadi pertanyaan saya adalah, apakah ada kasus ketika perlu atau berguna untuk menggunakan Threadobjek lama biasa daripada salah satu konstruksi di atas?

Tudor
sumber
4
Bukan untuk nitpick (atau mungkin ... :)), tapi itu. NET (yaitu tidak terbatas pada C #).
MetalMikester
11
Sejauh ini perwakilan rata-rata untuk pengguna yang menjawab pertanyaan ini adalah 64.5k.
Pertunjukan yang
1
@zzzzBov: Saya sedang berpikir untuk memposting jawaban saya sendiri tetapi sekarang saya tidak bisa karena takut menurunkan rata-rata :)
Brian Gideon
@BrianGideon, saya tidak melihat alasan mengapa hal itu menghentikan Anda atau orang lain untuk menjawab pertanyaan ini.
zzzzBov
@ Brian Gideon: Silakan posting. :)
Tudor

Jawaban:

107

Kelas Thread tidak dapat dibuat usang karena jelas ini adalah detail implementasi dari semua pola lain yang Anda sebutkan.

Tapi itu bukan pertanyaan Anda; pertanyaan Anda adalah

adakah kasus ketika perlu atau berguna untuk menggunakan objek Thread lama biasa sebagai ganti salah satu konstruksi di atas?

Tentu. Tepatnya pada kasus-kasus di mana salah satu konstruksi tingkat yang lebih tinggi tidak memenuhi kebutuhan Anda.

Saran saya adalah jika Anda berada dalam situasi di mana alat abstraksi tinggi yang ada tidak memenuhi kebutuhan Anda, dan Anda ingin menerapkan solusi menggunakan utas, maka Anda harus mengidentifikasi abstraksi yang hilang yang benar-benar Anda butuhkan , dan kemudian menerapkan abstraksi itu menggunakan utas , lalu gunakan abstraksi .

Eric Lippert
sumber
33
Semua nasihat bagus. Tetapi apakah Anda memiliki contoh di mana objek thread biasa yang lama mungkin lebih disukai daripada salah satu konstruksi yang lebih baru?
Robert Harvey
Debugging beberapa utas dengan masalah waktu untuk memuat beberapa kode dapat dilakukan jauh lebih mudah pada waktu-waktu tertentu daripada menggunakan konstruksi tingkat 'lebih tinggi' lainnya. Dan Anda membutuhkan pengetahuan dasar tentang cara bekerja dan menggunakan Threads.
CodingBarfield
Terima kasih atas jawabannya, Eric. Sangat mudah untuk melupakan, di bawah pemboman baru-baru ini tentang fitur threading baru, bahwa terkadang utas lama masih diperlukan. Yah, saya rasa pengetahuan tentang bare-metal berguna untuk dimiliki.
Tudor
15
@RobertHarvey: Tentu. Misalnya, jika Anda memiliki situasi "produsen / konsumen" klasik, di mana Anda ingin memiliki satu utas yang didedikasikan untuk mengeluarkan barang dari antrean pekerjaan dan utas lain yang didedikasikan untuk memasukkan barang ke antrean pekerjaan. Keduanya berputar selamanya; mereka tidak memetakan dengan baik ke tugas yang Anda lakukan untuk sementara waktu dan kemudian mendapatkan hasil. Anda tidak memerlukan kumpulan utas karena hanya ada dua utas. Dan Anda bisa pandai menggunakan kunci dan sinyal untuk memastikan bahwa antrian tetap aman tanpa memblokir thread lain untuk waktu yang lama.
Eric Lippert
@ Eric: Bukankah abstraksi seperti itu sudah ditawarkan oleh TPL Dataflow ?
Allon Guralnek
52

Utas adalah blok bangunan dasar untuk hal-hal tertentu (yaitu paralelisme dan asinkron) dan karenanya tidak boleh disingkirkan. Namun , bagi kebanyakan orang dan sebagian besar kasus penggunaan, ada hal-hal yang lebih tepat untuk digunakan yang Anda sebutkan, seperti kumpulan utas (yang menyediakan cara yang bagus untuk menangani banyak pekerjaan kecil secara paralel tanpa membebani mesin dengan menghasilkan 2000 utas sekaligus), BackgroundWorker( yang merangkum acara yang berguna untuk satu karya yang berumur pendek).

Tetapi hanya karena dalam banyak kasus itu lebih tepat karena mereka melindungi programmer dari menciptakan kembali roda yang tidak perlu, melakukan kesalahan bodoh dan sejenisnya, itu tidak berarti bahwa kelas Thread sudah usang. Ini masih digunakan oleh abstraksi yang disebutkan di atas dan Anda masih akan membutuhkannya jika Anda memerlukan kontrol yang lebih cermat atas utas yang tidak tercakup oleh kelas yang lebih khusus.

Dalam nada yang sama, .NETtidak melarang penggunaan array, meskipun List<T>lebih cocok untuk banyak kasus di mana orang menggunakan array. Hanya karena Anda mungkin masih ingin membuat hal-hal yang tidak tercakup oleh lib standar.

Joey
sumber
6
Hanya karena transmisi otomatis bekerja 99% dari waktu, tidak berarti tidak ada nilai dalam transmisi manual, bahkan mengabaikan preferensi pengemudi.
Guvante
4
Tidak terlalu paham dengan idiom mengemudi mengingat saya seorang pengendara sepeda dan pengguna angkutan umum. Tapi transmisi manual bukanlah inti dari transmisi otomatis - ini bukan tangan mekanis yang menggerakkan tongkat. Ini sebenarnya bukan abstraksi atas yang lain, sejauh yang saya bisa melihatnya.
Joey
2
Anda dapat mengganti kata "utas" dengan "kode tidak terkelola" di paragraf kedua dan kata itu akan tetap berdering benar
Conrad Frix
1
@ Joey: Oh, saya pikir yang Anda maksud adalah bagian usang, nilai memiliki kontrol yang sangat baik, dengan mengorbankan kegunaan, meskipun sebagian besar tidak akan membutuhkannya. BTW secara teknis bagian yang menarik dari transmisi manual adalah koplingnya.
Guvante
3
Jika kalian benar-benar ingin pergi dengan metafora transmisi, sebagian besar transmisi sekuensial (seperti transmisi SMG BMW) yang , pada kenyataannya, transmisi manual dengan komputer yang dioperasikan kopling dan selektor gigi. Mereka bukan sistem roda gigi planet seperti otomatis konvensional.
Adam Robinson
13

Taskdan Threadabstraksi yang berbeda. Jika Anda ingin membuat model utas, Threadkelas masih merupakan pilihan yang paling tepat. Misalnya jika Anda perlu berinteraksi dengan utas saat ini, saya tidak melihat jenis yang lebih baik untuk ini.

Namun, seperti yang Anda tunjukkan. NET telah menambahkan beberapa abstraksi khusus yang lebih disukai Threaddalam banyak kasus.

Brian Rasmussen
sumber
9

The Threadkelas tidak usang, masih berguna dalam keadaan khusus.

Di mana saya bekerja, kami menulis 'prosesor latar belakang' sebagai bagian dari sistem manajemen konten: layanan Windows yang memantau direktori, alamat email, dan umpan RSS, dan setiap kali sesuatu yang baru muncul, jalankan tugas di atasnya - biasanya untuk mengimpor data.

Upaya untuk menggunakan kumpulan thread untuk ini tidak berhasil: ia mencoba mengeksekusi terlalu banyak hal pada saat yang sama dan membuang disk, jadi kami menerapkan sistem polling dan eksekusi kami sendiri menggunakan Threadkelas secara langsung .

MiMo
sumber
Saya tidak tahu apakah menggunakan Thread secara langsung di sini adalah solusi yang tepat di sini, tetapi +1 untuk benar-benar memberikan contoh di mana pergi ke Thread diperlukan.
Oddthinking
6

Opsi baru membuat penggunaan langsung dan pengelolaan utas (mahal) lebih jarang.

orang yang, ketika dihadapkan dengan tugas yang melibatkan beberapa jenis eksekusi paralel, langsung menggunakan kelas Thread lama yang bagus.

Yang merupakan cara yang sangat mahal dan relatif rumit untuk melakukan sesuatu secara paralel.

Perhatikan bahwa biaya paling penting: Anda tidak dapat menggunakan utas lengkap untuk melakukan pekerjaan kecil, itu akan menjadi kontraproduktif. ThreadPool memerangi biaya, kelas Tugas, kerumitan (pengecualian, menunggu dan membatalkan).

Henk Holterman
sumber
5

Untuk menjawab pertanyaan " adakah kasus ketika perlu atau berguna untuk menggunakan objek Thread lama biasa ", saya akan mengatakan Thread lama biasa berguna (tetapi tidak perlu) ketika Anda memiliki proses yang berjalan lama yang Anda menangkan ' t pernah berinteraksi dengan dari utas yang berbeda.

Misalnya, jika Anda menulis aplikasi yang berlangganan untuk menerima pesan dari semacam antrian pesan dan aplikasi Anda akan melakukan lebih dari sekadar memproses pesan-pesan itu, maka akan berguna untuk menggunakan Thread karena utasnya akan mandiri (yaitu Anda tidak menunggu untuk diselesaikan), dan itu tidak berumur pendek. Menggunakan kelas ThreadPool lebih untuk mengantri sekumpulan item pekerjaan berumur pendek dan memungkinkan kelas ThreadPool mengelola secara efisien memproses masing-masing saat Thread baru tersedia. Tasks dapat digunakan di mana Anda akan menggunakan Thread secara langsung, tetapi dalam skenario di atas, saya rasa mereka tidak akan membeli banyak untuk Anda. Mereka membantu Anda berinteraksi dengan utas lebih mudah (yang skenario di atas tidak

Derek Greer
sumber
Saya setuju bahwa sebagian besar hal-hal paralelisme baru yang keren dirancang untuk tugas-tugas jangka pendek kecil yang mendetail, dan utas yang berjalan lama harus tetap diimplementasikan System.IO.Threading.Thread.
Ben Voigt
4

Mungkin bukan jawaban yang Anda harapkan, tetapi saya menggunakan Thread sepanjang waktu saat melakukan pengkodean terhadap .NET Micro Framework. MF cukup dikurangi dan tidak menyertakan abstraksi tingkat yang lebih tinggi dan kelas Thread sangat fleksibel saat Anda perlu mendapatkan kinerja terakhir dari CPU MHz rendah.

AngryHacker
sumber
Hei, itu sebenarnya contoh yang cukup bagus! Terima kasih. Saya belum memikirkan kerangka kerja yang tidak mendukung fitur baru.
Tudor
3

Anda dapat membandingkan kelas Thread dengan ADO.NET. Ini bukan alat yang disarankan untuk menyelesaikan pekerjaan, tetapi tidak usang. Alat lain dibangun di atasnya untuk memudahkan pekerjaan.

Tidak salah untuk menggunakan kelas Thread di atas hal-hal lain, terutama jika hal-hal itu tidak menyediakan fungsionalitas yang Anda butuhkan.

Kyle Trauberman
sumber
3

Itu belum pasti usang.

Masalah dengan aplikasi multithread adalah bahwa mereka sangat sulit untuk dilakukan dengan benar (seringkali perilaku tak tentu, input, output dan juga keadaan internal itu penting), jadi seorang programmer harus mendorong pekerjaan sebanyak mungkin ke kerangka / alat. Abstraksi itu. Namun, musuh bebuyutan abstraksi adalah kinerja.

Jadi pertanyaan saya adalah, apakah ada kasus ketika perlu atau berguna untuk menggunakan objek Thread lama biasa daripada salah satu konstruksi di atas?

Saya akan menggunakan Thread dan kunci hanya jika akan ada masalah kinerja yang serius, sasaran kinerja tinggi.

Lukasz Madon
sumber
3

Saya selalu menggunakan kelas Thread ketika saya perlu menghitung dan mengontrol thread yang telah saya putar. Saya menyadari bahwa saya dapat menggunakan threadpool untuk menampung semua pekerjaan saya yang luar biasa, tetapi saya tidak pernah menemukan cara yang baik untuk melacak berapa banyak pekerjaan yang sedang dilakukan atau apa statusnya.

Sebagai gantinya, saya membuat koleksi dan menempatkan utas di dalamnya setelah saya memutarnya - hal terakhir yang dilakukan utas adalah menghapus dirinya sendiri dari koleksi. Dengan begitu, saya selalu dapat mengetahui berapa banyak utas yang berjalan, dan saya dapat menggunakan koleksi tersebut untuk menanyakan apa yang dilakukannya. Jika ada kasus ketika saya perlu membunuh mereka semua, biasanya Anda harus menyetel semacam tanda "Batalkan" di aplikasi Anda, tunggu setiap utas memperhatikannya sendiri dan berhenti sendiri - dalam kasus saya, saya dapat menjalankan koleksi dan mengeluarkan Thread.Abort ke masing-masing secara bergantian.

Dalam hal ini, saya belum menemukan cara yang lebih baik untuk bekerja secara langsung dengan kelas Thread. Seperti yang disebutkan Eric Lippert, yang lainnya hanyalah abstraksi tingkat yang lebih tinggi, dan cocok untuk bekerja dengan kelas tingkat yang lebih rendah ketika penerapan tingkat tinggi yang tersedia tidak memenuhi kebutuhan Anda. Sama seperti Anda terkadang perlu melakukan panggilan Win32 API ketika .NET tidak memenuhi kebutuhan Anda secara tepat, akan selalu ada kasus di mana kelas Thread adalah pilihan terbaik meskipun ada "kemajuan" baru-baru ini.

SqlRyan
sumber