Daha yeni yapılardan biri yerine düz eski bir Thread nesnesinin kullanılmasının tercih edildiği durumlar var mı?


112

Blog yayınlarında ve burada SO'da ThreadC # 'ın son sürümlerinde sınıfın kullanımından kaçınan veya buna karşı tavsiyelerde bulunan birçok insan görüyorum (ve tabii ki 4.0+, Task& arkadaşların eklenmesiyle birlikte ). Daha önce bile, düz eski bir iş parçacığının işlevselliğinin birçok durumda ThreadPoolsınıf tarafından değiştirilebileceği gerçeği hakkında tartışmalar vardı .

Ayrıca, diğer özel mekanizmalar Threadsınıfı daha az çekici kılıyor, örneğin Timerçirkin Thread+ Sleepcombo'nun yerini alması , oysa bizim sahip olduğumuz GUI'ler BackgroundWorkervb.

Yine de, Threadbazı insanlar için (ben de dahil) çok tanıdık bir kavram gibi görünüyor, insanlar, bir tür paralel yürütmeyi içeren bir görevle karşılaştıklarında, doğrudan eski güzel Threadsınıfı kullanmaya atlayan insanlar . Son zamanlarda alışkanlıklarımı değiştirme zamanının gelip gelmediğini merak ediyordum.

Öyleyse sorum şu, Threadyukarıdaki yapılardan biri yerine düz eski bir nesneyi kullanmanın gerekli veya yararlı olduğu durumlar var mı?


4
Nitpick değil (veya belki ... :)), ama bu .NET (yani C # ile sınırlı değil).
MetalMikester

11
Şimdiye kadar bu soruyu cevaplayan bir kullanıcının ortalama temsilcisi 64.5k. Oldukça etkileyici bir gösteri
zzzzBov

1
@zzzzBov: Kendi cevabımı göndermeyi düşünüyordum ama şimdi ortalamayı düşürme korkusuyla yapamıyorum :)
Brian Gideon

@BrianGideon, bunun sizi veya başka birinin bu soruyu yanıtlamasını engellemesi için hiçbir neden göremiyorum.
zzzzBov

@Brian Gideon: Elbette, lütfen yayınlayın. :)
Tudor

Yanıtlar:


107

Thread sınıfı, bahsettiğiniz diğer tüm modellerin bir uygulama detayı olduğu için kullanılamaz hale getirilemez .

Ama bu gerçekten senin sorunun değil; senin sorunuz

Yukarıdaki yapılardan biri yerine düz eski bir Thread nesnesini kullanmanın gerekli veya yararlı olduğu durumlar var mı?

Elbette. Tam olarak, üst düzey yapılardan birinin ihtiyaçlarınızı karşılamadığı durumlarda.

Benim tavsiyem, kendinizi mevcut yüksek soyutlama araçlarının ihtiyaçlarınızı karşılamadığı bir durumda bulursanız ve ipleri kullanarak bir çözüm uygulamak istiyorsanız, gerçekten ihtiyacınız olan eksik soyutlamayı belirlemeli ve sonra bu soyutlamayı uygulamalısınız. konuları kullanarak ve sonra soyutlamayı kullanın .


33
Hepsi iyi tavsiyeler. Ancak, düz eski bir evre nesnesinin daha yeni yapılardan birine tercih edilebileceği bir örneğiniz var mı?
Robert Harvey

Bazı kodları yüklemek için zamanlama sorunları olan birden fazla iş parçacığının hata ayıklaması, bazen diğer 'daha yüksek' seviyeli yapıları kullanmaktan çok daha kolay yapılabilir. Ve yine de Threads çalışma ve kullanma konusunda temel bilgiye ihtiyacınız var.
CodingBarfield

Cevabın için teşekkürler Eric. Yeni iş parçacığı özellikleri hakkındaki son bombardımanda bazen eski iş parçacığına hala ihtiyaç duyulduğunu unutmak gerçekten çok kolay. Sanırım çıplak metal hakkında bilgi sahibi olmak yararlıdır.
Tudor

15
@RobertHarvey: Elbette. Örneğin, klasik bir "üretici / tüketici" durumunuz varsa, bir iş kuyruğundan bir şeyler almaya adanmış bir iş parçacığına ve işleri bir iş kuyruğuna yerleştirmeye adanmış başka bir iş parçacığına sahip olmak istiyorsanız. Her iki döngü de sonsuza kadar; bir süre yaptığınız görevlerle iyi bir şekilde eşleşmezler ve sonra bir sonuç alırlar. Yalnızca iki iş parçacığı olduğu için iş parçacığı havuzuna ihtiyacınız yoktur. Ayrıca, diğer iş parçacığını uzun süre engellemeden kuyruğun güvende olmasını sağlamak için kilitleri ve sinyalleri kullanma konusunda akıllı olabilirsiniz.
Eric Lippert

@Eric: TPL Dataflow tarafından zaten böyle bir soyutlama sunulmamış mı?
Allon Guralnek

52

İplikler, belirli şeyler (yani paralellik ve eşzamansızlık) için temel bir yapı taşıdır ve bu nedenle kaldırılmamalıdır. Bununla birlikte , çoğu insan ve çoğu kullanım durumu için, bahsettiğiniz daha uygun şeyler vardır, örneğin iplik havuzları (bu, aynı anda 2000 iş parçacığı oluşturarak makineyi aşırı yüklemeden paralel olarak birçok küçük işi ele almanın güzel bir yolunu sağlar), BackgroundWorker( tek bir kısa ömürlü çalışma için yararlı olayları içerir).

Ancak çoğu durumda bunlar programcıyı gereksiz yere tekerleği yeniden icat etmekten, aptalca hatalar yapmaktan ve benzerlerinden koruduğu için daha uygun olduğu için, bu Thread sınıfının modası geçmiş olduğu anlamına gelmez. Hala yukarıda belirtilen soyutlamalar tarafından kullanılmaktadır ve daha özel sınıflar tarafından kapsanmayan iş parçacıkları üzerinde ayrıntılı kontrole ihtiyacınız varsa yine de ihtiyacınız olacaktır.

Benzer bir şekilde, insanların dizileri kullandığı birçok duruma daha uygun .NETolmasına rağmen, dizilerin kullanımını yasaklamaz List<T>. Bunun nedeni, standart kitaplığın kapsamadığı şeyleri hala inşa etmek isteyebilirsiniz.


6
Otomatik bir şanzımanın zamanın% 99'unda çalışması, manuel şanzımanlarda değer olmadığı anlamına gelmez, hatta sürücü tercihini görmezden gelir.
Guvante

4
Bisikletçi ve toplu taşıma kullanıcısı olduğum için araba kullanma deyimlerine pek aşina değilim. Ancak manuel bir şanzıman, otomatik olanın merkezinde değildir - bir çubuğu hareket ettiren mekanik bir el değildir. Görebildiğim kadarıyla, aslında diğerinin üzerinde bir soyutlama değil.
Joey

2
İkinci paragrafta "iş parçacığı" kelimesini "yönetilmeyen kod" ile değiştirebilirsiniz ve yine de doğru
çınlayacaktır

1
@Joey: Oh, birçoğunun asla ihtiyacı olmasa da, kullanılabilirlik pahasına, eskimiş parçayı, ince taneli kontrole sahip olmanın değerini kastettiğinizi düşünmüştüm. BTW teknik olarak manuel şanzımanın ilginç kısmı zaten debriyaj.
Guvante

3
Siz gerçekten iletim metaforu ile gitmek isterseniz, (örneğin BMW'nin SMG şanzıman gibi) en sıralı yayınlar vardır , aslında, bir bilgisayar ile manuel şanzımanlar debriyaj ve vites işletilmektedir. Geleneksel otomatikler gibi gezegen dişli sistemleri değiller.
Adam Robinson

13

Taskve Threadfarklı soyutlamalardır. Bir iş parçacığı modellemek istiyorsanız, Threadsınıf yine de en uygun seçimdir. Örneğin, mevcut iş parçacığı ile etkileşime girmeniz gerekiyorsa, bunun için daha iyi türler görmüyorum.

Bununla birlikte, sizin de belirttiğiniz gibi .NET, Threadbirçok durumda tercih edilen birkaç özel soyutlama eklemiştir .


9

ThreadSınıf hala özel durumlarda yararlıdır, eskimiş değildir.

Çalıştığım yerde, içerik yönetim sisteminin bir parçası olarak bir 'arka plan işlemcisi' yazdık: dizinleri, e-posta adreslerini ve RSS beslemelerini izleyen ve her yeni bir şey göründüğünde üzerinde bir görev yürüten bir Windows hizmeti - genellikle veri.

Bunun için iş parçacığı havuzunu kullanma girişimleri işe yaramadı: aynı anda çok fazla şey yürütmeye ve diskleri çöpe atmaya çalışıyor, bu yüzden doğrudan Threadsınıfı kullanarak kendi yoklama ve yürütme sistemimizi uyguladık .


Burada Thread'ı doğrudan kullanmanın doğru çözüm olup olmadığı hakkında hiçbir fikrim yok, ama gerçekten Thread'e gitmenin gerekli olduğu bir örnek vermek için +1.
Oddthinking

6

Yeni seçenekler , (pahalı) iş parçacıklarının doğrudan kullanımını ve yönetimini daha seyrek hale getirir .

Bir tür paralel yürütme içeren bir görevle karşılaştıklarında, doğrudan eski güzel Thread sınıfına atlayan insanlar.

Bu, işleri paralel olarak yapmanın çok pahalı ve nispeten karmaşık bir yoludur.

Masrafın en önemli olduğunu unutmayın: Küçük bir işi yapmak için tam bir iş parçacığı kullanamazsınız, ters etki yapar. ThreadPool maliyetlerle mücadele eder, Görev sınıfı karmaşıklıklarla (istisnalar, bekleme ve iptal etme).


5

" Düz, eski bir Thread nesnesini kullanmanın gerekli veya yararlı olduğu durumlar var mı " sorusuna cevap vermek için , kazandığınız uzun süren bir işleminiz olduğunda düz eski bir Thread yararlıdır (ancak gerekli değildir) ' t Asla farklı bir iş parçacığından etkileşim.

Örneğin, bir tür mesaj kuyruğundan mesaj almaya abone olan bir uygulama yazıyorsanız ve uygulamanız bu mesajları işlemekten daha fazlasını yapacaksa, o zaman bir İş Parçacığı kullanmak yararlı olacaktır çünkü iş parçacığı kendi kendine yeten (yani bitmesini beklemiyorsunuz) ve kısa ömürlü değil. ThreadPool sınıfını kullanmak, bir grup kısa ömürlü çalışma öğesini sıraya koymak ve ThreadPool sınıfının her birini yeni bir Thread mevcutken verimli bir şekilde işlemeyi yönetmesine izin vermek için daha fazladır. Görevler, Thread'ı doğrudan kullanacağınız yerde kullanılabilir, ancak yukarıdaki senaryoda size fazla bir şey satın alacaklarını sanmıyorum. İş parçacığı ile daha kolay etkileşim kurmanıza yardımcı olurlar (yukarıdaki senaryo bunu yapmaz)


Harika yeni paralellik şeylerinin çoğunun kısa vadeli küçük ince taneli görevler etrafında tasarlandığına ve uzun süren iş parçacıklarıyla birlikte uygulanması gerektiğine katılıyorum System.IO.Threading.Thread.
Ben Voigt

4

Muhtemelen beklediğiniz cevap bu değildi, ancak .NET Micro Framework'e karşı kodlama yaparken her zaman Thread kullanıyorum. MF oldukça kısaltılmıştır ve daha yüksek seviyeli soyutlamalar içermez ve Thread sınıfı, düşük MHz CPU'dan son performans bitini almanız gerektiğinde süper esnektir.


Hey, bu aslında oldukça iyi bir örnek! Teşekkürler. Yeni özellikleri desteklemeyen çerçeveler hakkında düşünmedim.
Tudor

3

Thread sınıfını ADO.NET ile karşılaştırabilirsiniz. İşi yapmak için önerilen araç değil, ancak eski değil. İşi kolaylaştırmak için diğer araçlar bunun üzerine inşa edilmiştir.

Diğer şeyler yerine Thread sınıfını kullanmak yanlış değildir, özellikle bu şeyler ihtiyacınız olan bir işlevselliği sağlamıyorsa.


3

Kesinlikle modası geçmiş değil.

Çok iş parçacıklı uygulamalarla ilgili sorun, doğruya ulaşmanın çok zor olmasıdır (genellikle belirsiz davranış, girdi, çıktı ve ayrıca dahili durum önemlidir), bu nedenle bir programcı, çerçeveye / araçlara mümkün olduğunca çok çalışmalıdır. Soyutlayın. Ancak, soyutlamanın ölümcül düşmanı performanstır.

Öyleyse sorum şu, yukarıdaki yapılardan biri yerine düz eski bir Thread nesnesini kullanmanın gerekli veya yararlı olduğu durumlar var mı?

Sadece ciddi performans sorunları, yüksek performans hedefleri varsa, Dişler ve kilitlerle giderdim.


3

Sayımı tutmam ve döndürdüğüm konuları kontrol etmem gerektiğinde her zaman Thread sınıfını kullandım. Tüm olağanüstü işlerimi tutmak için iş parçacığı havuzunu kullanabileceğimi fark ettim, ancak şu anda ne kadar iş yapıldığını veya durumun ne olduğunu takip etmenin iyi bir yolunu bulamadım.

Bunun yerine, bir koleksiyon oluşturup iplikleri onlara döndürdükten sonra yerleştiriyorum - bir iş parçacığının yaptığı en son şey kendini koleksiyondan çıkarmaktır. Bu şekilde, kaç iş parçacığının çalıştığını her zaman söyleyebilirim ve koleksiyonu her birine ne yaptığını sormak için kullanabilirim. Hepsini öldürmem gereken bir durum varsa, normalde uygulamanızda bir tür "İptal" bayrağı ayarlamanız gerekir, her iş parçacığının bunu kendi kendine fark etmesini ve kendi kendine sonlandırmasını bekleyin - benim durumumda, ben koleksiyonu gezebilir ve sırayla her birine bir Thread.Abort verebilir.

Bu durumda, doğrudan Thread sınıfıyla çalışmaktan daha iyi bir yol bulamadım. Eric Lippert'in bahsettiği gibi, diğerleri sadece üst düzey soyutlamalardır ve mevcut üst düzey uygulamalar ihtiyacınızı karşılamadığında alt düzey sınıflarla çalışmak uygundur. NET ihtiyaçlarınızı tam olarak karşılamadığında bazen Win32 API çağrıları yapmanız gerektiği gibi, son "ilerlemelere" rağmen her zaman Thread sınıfının en iyi seçim olduğu durumlar olacaktır.

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.