İş parçacıkları kodun yürütülmesini hızlandırabilirken, gerçekten gerekli mi? Her kod parçası tek bir iş parçacığı kullanılarak yapılabilir mi, yoksa yalnızca birden çok iş parçacığı kullanılarak gerçekleştirilebilecek bir şey var mı?
Her şeyden önce, iş parçacıkları kodun yürütülmesini hızlandıramaz. Bilgisayarı daha hızlı çalıştırmazlar. Tüm yapabilecekleri, aksi takdirde boşa harcanacak zamanı kullanarak bilgisayarın verimliliğini artırmaktır. Belirli işlem türlerinde bu optimizasyon verimliliği artırabilir ve çalışma süresini azaltabilir.
Basit cevap evet. Tek bir iş parçacığında çalıştırılacak herhangi bir kodu yazabilirsiniz. İspat: Tek bir işlemci sistemi talimatları yalnızca doğrusal olarak çalıştırabilir. İşletim sisteminin işlem kesmeleri, geçerli iş parçacığının durumunu kaydetme ve başka bir iş parçacığı başlatma işlemleri tarafından birden çok yürütme satırına sahip olmak.
karmaşık cevap ... daha karmaşık! Çok iş parçacıklı programların genellikle doğrusal programlardan daha verimli olabilmesinin nedeni, bir donanım "sorunu" ndan kaynaklanmaktadır. CPU, hesaplamaları bellek ve sabit depolama GÇ değerinden daha hızlı gerçekleştirebilir. Bu nedenle, örneğin, bir "ekle" talimatı bir "getirme" den çok daha hızlı yürütülür. Önbellekler ve özel program talimatı getirme (burada tam terimden emin değilim) bir dereceye kadar bununla mücadele edebilir, ancak hız sorunu devam eder.
İş parçacığı oluşturma, IO komutları tamamlanırken CPU ile ilişkili talimatlar için CPU'yu kullanarak bu uyumsuzlukla mücadele etmenin bir yoludur. Tipik bir iş parçacığı yürütme planı muhtemelen şu şekildedir: Verileri alma, veri işleme, veri yazma. getirme ve yazma 3 döngü alır ve işleme, açıklayıcı amaçlar için bir alır. Bilgisayar okurken veya yazarken, her biri 2 döngü için hiçbir şey yapıyor görebilirsiniz? Açıkçası tembel olmak ve optimizasyon kamçı kırmak gerekir!
Bu boşa harcanan zamanı kullanmak için iş parçacığı kullanarak işlemi yeniden yazabiliriz:
Ve bunun gibi. Açıkçası bu biraz çelişkili bir örnek, ancak bu tekniğin aksi halde IO'yu beklerken harcayacağı zamanı nasıl kullanabileceğini görebilirsiniz.
Yukarıda gösterildiği gibi iş parçacığının yalnızca ağır IO bağlı süreçlerde verimliliği artırabileceğini unutmayın. Bir program esas olarak bir şeyleri hesaplıyorsa, çok fazla "delik" olmayacak Ayrıca, iş parçacıkları arasında geçiş yaparken birkaç yönerge yükü vardır.Çok fazla iş parçacığı çalıştırırsanız, CPU, zaman anahtarlama çoğu zaman harcayacak ve aslında sorun üzerinde çok çalışma değil harcayacaktır.Bu thrashing =.
Her şey tek çekirdekli işlemci için iyi ve iyi, ancak çoğu modern işlemcinin iki veya daha fazla çekirdeği var. İş parçacıkları hala aynı amaca hizmet ediyor - CPU kullanımını en üst düzeye çıkarmak için, ancak bu sefer aynı zamanda. Bu olabilir azalma ancak bilgisayar aslında bağlam değiştirme değil, çoklu görevlidir.
Birden çok çekirdeğe sahip olan dişler, işi iki çekirdek arasında bölme yöntemi sağlar. Yukarıdakiler yine de her bir çekirdek için geçerlidir; Bir çekirdek üzerinde iki iplik ile maksimum verimlilik çalıştıran bir program büyük olasılıkla iki çekirdek üzerinde yaklaşık dört iplik ile en yüksek verimlilikte çalışacaktır. (Verimlilik burada minimum NOP talimatı yürütmeleri ile ölçülür.)
Birden fazla çekirdek üzerinde diş çekme ile ilgili problemler (tek bir çekirdeğin aksine) genellikle donanım tarafından halledilir. CPU, okuma/yazma işleminden önce uygun bellek konumlarını kilitlediğinden emin olacaktır. (Bunun için bellekte özel bir bayrak biti kullandığını okudum, ancak bu çeşitli şekillerde gerçekleştirilebilir.) Daha yüksek seviyeli dillere sahip bir programcı olarak, iki çekirdek üzerinde daha fazla şey hakkında endişelenmenize gerek yok. biri ile yapmak zorunda kalacak.
TL; DR: İş parçacıkları, bilgisayarın birkaç görevi eşzamansız olarak işlemesine izin vermek için bölünebilir. Bu, bir işlem bir kaynak beklerken kilitlemek yerine bilgisayarın tüm işlem süresini kullanarak maksimum verimlilikte çalışmasını sağlar.
Tek bir iş parçacığının yapamayacağı birden çok iş parçacığı ne yapabilir?
Hiçbir şey değil.
Basit kanıt çizimi:
Bununla birlikte, orada gizli büyük bir varsayım olduğunu unutmayın: yani kullanılan dil içinde tek iş parçacığı Turing-complete.
Yani, daha ilginç bir soru şöyle olacaktır: "Turing-tamamlanmamış bir dile just çoklu-iş parçacığı eklemek Turing-tamamlama yapabilir mi?" Ve inanıyorum, cevap "Evet".
Toplam Fonksiyonel Dilleri ele alalım. [Aşina olmayanlar için: Tıpkı Fonksiyonel Programlama Fonksiyonlarla Programlama gibi, Toplam Fonksiyonel Programlama Toplam Fonksiyonlarla Programlama gibi.]
Toplam İşlevsel Diller açıkçası Turing-complete değildir: TFPL'ye sonsuz bir döngü yazamazsınız (aslında, bu tanım "toplam"), ancak siz yapabilirsiniz Bir Turing Makinesinde, bir TFPL'de yazılamayan ancak bir UTM'de yazılabilen en az bir program vardır, bu nedenle TFPL'ler UTM'lerden daha az hesaplama gücüne sahiptir.
Ancak, bir TFPL'ye iş parçacığı ekler eklemez sonsuz döngüler elde edersiniz: döngünün her yinelemesini yeni bir iş parçacığında yapın. Her bir iş parçacığı her zaman bir sonuç döndürür, bu nedenle Toplam'dır, ancak her iş parçacığı da yeni iş parçacığını sonraki yinelemesini, reklam sonsuzunu yürütür.
I düşün bu dilin Turing-complete olacağını söyledi.
En azından orijinal soruya cevap veriyor:
Tek bir iş parçacığının yapamayacağı birden çok iş parçacığı ne yapabilir?
Eğer sonsuz döngüler yapamayan bir diliniz var, o zaman çoklu iş parçacığı sonsuz döngüler yapmanızı sağlar.
Elbette, bir iş parçacığının doğmasının bir yan etkisi olduğunu ve bu nedenle genişletilmiş dilimizin artık Toplam olmadığını, artık İşlevsel olmadığını da unutmayın.
Teoride, çok iş parçacıklı bir programın yaptığı her şey tek iş parçacıklı bir programla da yapılabilir, sadece daha yavaş.
Pratikte, hız farkı o kadar büyük olabilir ki, görev için tek iş parçacıklı bir programı kullanmanın bir yolu yoktur. Örneğin. her gece toplu veri işleme işiniz varsa ve tek bir iş parçacığında bitirmek 24 saatten fazla sürüyorsa, çok iş parçacıklı hale getirmekten başka seçeneğiniz yoktur. (Uygulamada, eşik muhtemelen daha azdır: genellikle bu tür güncelleme görevleri, kullanıcılar sistemi tekrar kullanmaya başlamadan önce sabahın erken saatlerinde bitmelidir. Ayrıca, aynı gece boyunca bitmesi gereken diğer görevler de bunlara bağlı olabilir. kullanılabilir çalışma süresi birkaç saat/dakika kadar düşük olabilir.)
Birden çok iş parçacığında bilgi işlem çalışması yapmak dağıtılmış işlem biçimidir; işi birden çok iş parçacığına dağıtıyorsunuz. Dağıtılmış işleme başka bir örnek (birden çok iş parçacığı yerine birden çok bilgisayar kullanarak) SETI ekran koruyucudur: tek bir işlemcideki çok fazla ölçüm verisinin çok uzun zaman alacağını ve araştırmacıların emeklilikten önce sonuçları görmeyi tercih edeceğini ;-) Ancak, süper bilgisayar kiralamak için bütçeniz yok, bu yüzden işi ucuz hale getirmek için milyonlarca ev bilgisayarına dağıtıyorlar.
İş parçacıkları sıralı hesaplamadan küçük bir adım gibi görünse de, aslında büyük bir adımı temsil ederler. Sıralı hesaplamanın en temel ve çekici özelliklerini atarlar: anlaşılabilirlik, öngörülebilirlik ve determinizm. Bir hesaplama modeli olarak iplikler çılgınca belirsizdir ve programcının işi bu belirsizliği budamadan bir hale gelir.
- İpliklerle İlgili Sorun (www.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf).
İşleri birden çok çekirdeğe dağıtabilmeniz için dişleri kullanarak elde edilebilecek bazı performans avantajları olsa da, bunlar genellikle harika bir fiyata gelir.
Henüz burada bahsedilmeyen konuları kullanmanın dezavantajlarından biri, tek iş parçacıklı işlem alanlarıyla elde ettiğiniz kaynak bölümlendirmesinin kaybıdır. Örneğin, bir segfault durumunda karşılaştığınızı varsayalım. Bazı durumlarda, çok işlemli bir uygulamada bundan kurtulmak mümkündür, çünkü arızalı çocuğun ölmesine ve yeni bir çocuğu yeniden doğmasına izin vermeniz yeterlidir. Apache'nin prefork arka ucunda durum böyledir. Bir httpd örneği ortaya çıktığında, en kötü durum, belirli HTTP isteğinin bu işlem için bırakılabileceği, ancak Apache yeni bir çocuk oluşturur ve genellikle yeniden gönderilirse ve servis yapılırsa istek olur. Sonuç, Apache'nin bir bütün olarak hatalı iş parçacığıyla indirilmemesidir.
Bu senaryoda dikkat edilmesi gereken bir diğer nokta da bellek sızıntılarıdır. Bir iş parçacığının kilitlenmesini (UNIX'te, bazı belirli sinyallerden - hatta segfault/fpviolation - hatta kurtarma) mümkün olduğu kadar iyi işleyebileceğiniz bazı durumlar vardır, ancak bu durumda bile, o iş parçacığı tarafından ayrılan tüm belleği sızdırmış olabilirsiniz (malloc, yeni vb.). İşleminiz devam ederken, her hata/kurtarma işleminde zamanla daha fazla bellek sızdırıyor. Yine, Apache'nin bellek havuzlarını kullanması gibi bunu en aza indirmenin bazı dereceleri vardır. Ancak bu yine de iş parçacığının kullanıyor olabileceği üçüncü taraf kütüphaneleri tarafından ayrılan belleğe karşı koruma sağlamaz.
Ve bazı insanların işaret ettiği gibi, senkronizasyon ilkellerini anlamak belki de gerçekten doğru olan en zor şeydir. Bu sorun tek başına - genel mantığı tüm kodlarınız için doğru yapmak - büyük bir baş ağrısı olabilir. Gizemli kilitlenmeler en tuhaf zamanlarda gerçekleşmeye eğilimlidir ve bazen programınız üretimde çalışana kadar bile hata ayıklamayı daha da zorlaştırır. Buna, senkronizasyon ilkelerinin genellikle platforma (Windows ve POSIX'e göre) büyük ölçüde değiştiği ve hata ayıklamanın genellikle daha zor olabileceği gibi, herhangi bir zamanda yarış koşulları (başlatma/başlatma, çalışma zamanı ve kapatma) olasılığı da eklenebilir, iş parçacığı ile programlama yeni başlayanlar için gerçekten çok az merhamet var. Ve uzmanlar için bile, sadece diş çekme bilgisinin genel olarak karmaşıklığı en aza indirmediği için hala az merhamet vardır. Her bir dişli kod satırı bazen programın genel karmaşıklığını katlanarak birleştirir ve aynı zamanda herhangi bir zamanda gizli bir kilitlenme veya garip yarış koşulunun ortaya çıkma olasılığını artırır. Bunları ortaya çıkarmak için test senaryoları yazmak da çok zor olabilir.
Bu nedenle Apache ve PostgreSQL gibi bazı projeler çoğunlukla süreç tabanlıdır. PostgreSQL her arka uç iş parçacığını ayrı bir işlemde çalıştırır. Tabii ki bu hala senkronizasyon ve yarış koşulları sorununu hafifletmiyor, ancak biraz koruma sağlıyor ve bazı şeyleri basitleştiriyor.
Her biri tek bir yürütme iş parçacığını çalıştıran birden çok işlem, tek bir işlemde çalışan birden çok iş parçacığından çok daha iyi olabilir. AMQP (RabbitMQ, Qpid, vb.) Ve ZeroMQ gibi yeni eşler arası kodların çoğunun ortaya çıkmasıyla, iş parçacıklarını farklı işlem alanlarına ve hatta makinelere ve ağlara bölmek çok daha kolaydır. Ama yine de, gümüş bir kurşun değil. Bununla başa çıkmak için hala karmaşıklık var. Değişkenlerinizin bir kısmını işlem alanından ağa taşıyabilirsiniz.
Sonuç olarak, iş parçacığı alanına girme kararı hafif değildir. Bu bölgeye adım attığınızda, neredeyse anında her şey daha karmaşık hale gelir ve tüm yeni türler hayatınıza girer. Eğlenceli ve havalı olabilir, ama nükleer enerji gibi - işler ters gittiğinde kötü ve hızlı gidebilirler. Yıllar önce eleştirel eğitim konusunda bir ders aldığımı hatırlıyorum ve İkinci Dünya Savaşı'ndaki laboratuvarlarda plütonyum ile oynayan Los Alamos'taki bazı bilim adamlarının resimlerini gösterdiler. Birçoğu bir maruz kalma olayına karşı çok az önlem aldı veya hiç önlem almadı ve göz açıp kapayıncaya kadar - tek bir parlak, ağrısız flaşta, hepsi onlar için bitecekti. Günler sonra öldüler. Richard Feynman daha sonra buna " ejderhanın kuyruğunu gıdıklayan " demişti. İplikler ile oynamak böyle bir şey olabilir (en azından benim için zaten). İlk başta oldukça zararsız görünüyor ve ısırdığınızda, şeylerin ne kadar çabuk ekşime gittiğine dair kafanızı tırmalamak. Ama en azından evreler seni öldürmeyecek.
İlk olarak, tek iş parçacıklı bir uygulama hiçbir zaman çok çekirdekli bir CPU veya hiper iş parçacığından yararlanamaz. Ancak tek bir çekirdekte bile, çoklu iş parçacığı yapan tek iş parçacıklı CPU'nun avantajları vardır.
Alternatifi ve bunun sizi mutlu edip etmediğini düşünün. Aynı anda çalışması gereken birden fazla göreviniz olduğunu varsayalım. Örneğin, iki farklı sistemle iletişim kurmaya devam etmelisiniz. Bunu çoklu iş parçacığı olmadan nasıl yaparsınız? Muhtemelen kendi zamanlayıcınızı yaratır ve gerçekleştirilmesi gereken farklı görevleri çağırmasına izin verirsiniz. Bu, görevlerinizi parçalara ayırmanız gerektiği anlamına gelir. Muhtemelen parçalarınızın çok fazla zaman almadığından emin olmanız gereken bazı gerçek zamanlı kısıtlamaları karşılamanız gerekir. Aksi takdirde zamanlayıcının süresi diğer görevlerde dolar. Bu, bir görevi bölmeyi zorlaştırır. Kendinizi yönetmek için ne kadar fazla görev yapmanız gerekiyorsa, o kadar fazla bölünme yapmanız gerekir ve zamanlayıcınız tüm kısıtlamaları karşılamak için o kadar karmaşık hale gelir.
Birden fazla iş parçacığınız olduğunda, yaşam daha kolay olabilir. Önleyici bir zamanlayıcı her zaman bir iş parçacığını durdurabilir, durumunu koruyabilir ve başka bir iş parçasını yeniden başlatabilir. İpliğiniz sıra geldiğinde yeniden başlayacaktır. Avantajları: Zamanlayıcı yazmanın karmaşıklığı sizin için zaten yapılmıştır ve görevlerinizi bölmeniz gerekmez. Ayrıca, zamanlayıcı sizin bile farkında olmadığınız süreçleri/iş parçacıklarını yönetebilir. Ve ayrıca, bir iş parçacığının herhangi bir şey yapması gerekmediğinde (bazı olayları beklemektedir) hiçbir CPU döngüsü gerektirmez. Bu, aşağı tek iş parçacıklı zamanlayıcınızı oluştururken bunu başarmak o kadar kolay değil. (bir şeyleri uyutmak o kadar zor değil, ama nasıl uyanıyor?)
Çok iş parçacıklı geliştirmenin dezavantajı, eşzamanlılık sorunları, kilitleme stratejileri ve benzerlerini anlamanız gerektiğidir. Hatasız çok iş parçacıklı kod geliştirmek oldukça zor olabilir. Ve hata ayıklama daha da zor olabilir.
sadece birden çok iş parçacığı kullanılarak gerçekleştirilebilecek bir şey var mı?
Evet. Tek bir iş parçacığı ile birden fazla CPU veya CPU çekirdeğinde kod çalıştıramazsınız.
Birden fazla CPU/çekirdek olmadan, iş parçacıkları, bir sunucuda istemci işlemesi gibi kavramsal olarak paralel çalışan kodu basitleştirebilir - ancak aynı şeyi iş parçacıkları olmadan da yapabilirsiniz.
Konular sadece hız değil aynı zamanda eşzamanlılık ile ilgilidir.
@Peter'in önerdiği gibi bir toplu iş uygulamanız yoksa, bunun yerine WPF gibi bir GUI araç setiniz varsa, yalnızca bir iş parçacığıyla kullanıcılar ve iş mantığı ile nasıl etkileşime geçebilirsiniz?
Ayrıca, bir Web Sunucusu oluşturduğunuzu varsayalım. Tek bir iş parçacığıyla (başka işlem olmadığını varsayarak) aynı anda birden fazla kullanıcıya nasıl hizmet edersiniz?
Sadece bir iş parçacığının yeterli olmadığı birçok senaryo vardır. Bu yüzden 50'den fazla çekirdeğe ve yüzlerce iş parçacığına sahip Intel MIC işlemci gibi son gelişmeler gerçekleşiyor.
Evet, paralel ve eşzamanlı programlama zordur. Ama gerekli.
Multi-Threading, GUI arayüzünün uzun işleme operasyonları sırasında hala duyarlı olmasına izin verebilir. Çoklu iş parçacığı olmadan, kullanıcı uzun bir işlem çalışırken kilitli bir formu izlemeye takılıp kalır.
Çok iş parçacıklı kod, program mantığını kilitleyebilir ve eski verilere tek iş parçacıklarının yapamayacağı şekilde erişebilir.
İş parçacıkları, ortalama bir programcının hata ayıklaması beklenen bir şeyden belirsiz bir hata alabilir ve bir uyarı programcısı sadece doğru an.
diğer girdilere (GUI veya diğer bağlantılar) yanıt vermesi gereken engelleme ile ilgilenen uygulamalar IO) tek başına işlenemez
engellemeden ne kadar okunabileceğini görmek için IO lib) kontrol yöntemlerinin eklenmesi bu konuda yardımcı olabilir, ancak pek çok kütüphane bu konuda tam garanti vermez
Çok iyi cevaplar ama herhangi bir ifadeyi tam olarak istediğimden emin değilim - Belki de bu ona bakmak için farklı bir yol sunar:
Konular yalnızca Nesneler veya Aktörler veya döngüler için bir programlama basitleştirmesidir (Evet, if/goto ile uygulayabileceğiniz döngülerle uyguladığınız her şey).
İş parçacıkları olmadan bir durum motoru uygularsınız. Bunu birçok kez yapmak zorunda kaldım (ilk kez bunu hiç duymadım - sadece bir "Devlet" değişkeni tarafından kontrol edilen büyük bir anahtar ifadesi yaptım). Devlet makineleri hala oldukça yaygındır, ancak can sıkıcı olabilir. İplikler ile kazan plakasının büyük bir kısmı gider.
Ayrıca, bir dilin çalışma zamanı uygulamasını çoklu CPU dostu parçalara bölmesini kolaylaştırırlar (Aktörler de inanıyorum).
Java, işletim sisteminin HERHANGİ bir iş parçacığı desteği sağlamadığı sistemlerde "Yeşil" iş parçacıkları sağlar. Bu durumda, bunların bir programlama soyutlamasından başka bir şey olmadığını açıkça görmek daha kolaydır.
İlk olarak, iş parçacıkları aynı anda iki veya daha fazla şey yapabilir (birden fazla çekirdeğiniz varsa). Bunu birden çok işlemle de yapabilirsiniz, ancak bazı görevler birden çok işlem üzerinde çok iyi dağıtılmaz.
Ayrıca, bazı görevlerin içinde kolayca kaçınamayacağınız boşluklar vardır. Örneğin, diskteki bir dosyadan veri okumak ve aynı zamanda işleminizin başka bir şey yapmasını sağlamak zordur. Göreviniz mutlaka diskten çok fazla veri okuma gerektiriyorsa, işleminiz ne yaparsanız yapın diski beklemek için çok zaman harcayacaktır.
İkinci olarak, iş parçacıkları, kodunuzu performans açısından kritik olmayan büyük miktarlarda optimize etmek zorunda kalmanıza izin vermez. Yalnızca tek bir iş parçacığınız varsa, her kod parçası performans açısından kritik öneme sahiptir. Engellenirse batırılırsınız - bu işlem tarafından yapılacak hiçbir işlem ileriye doğru ilerleme kaydedemez. Bir iş parçacığında, bir blok yalnızca iş parçacığının ve diğer iş parçacıklarının gelebileceğini etkiler ve bu işlem tarafından yapılması gereken görevler üzerinde çalışır.
İyi bir örnek, nadiren yürütülen hata işleme kodudur. Bir görevin çok seyrek bir hatayla karşılaştığını ve bu hatayı işlemek için kodun belleğe girmesi gerektiğini varsayalım. Disk meşgulse ve işlemin yalnızca tek bir iş parçacığı varsa, bu hatayı işleyen kod belleğe yükleninceye kadar ileri ilerleme sağlanmaz. Bu, hızlı yanıt verebilir.
Başka bir örnek, çok nadiren bir veritabanı araması yapmanız gerekebileceğidir. Veritabanının yanıt vermesini beklerseniz, kodunuz büyük bir gecikmeye neden olur. Ancak, tüm bu kodları eşzamansız yapma zahmetine girmek istemezsiniz çünkü bu aramaları yapmanız çok nadirdir. Bu işi yapmak için bir iplik ile her iki dünyanın en iyisini elde edersiniz. Bu işi yapmak için bir iş parçacığı olması gerektiği gibi performans kritik hale getirir.
İşletim sistemleri, her bir iş parçacığının çalışma zamanını ve ardından önlenmesini sağlayan zaman dilimleme kavramını kullanır. Böyle bir yaklaşım, iş parçacığının şu anda olduğu gibi yerini alabilir, ancak her uygulamada kendi zamanlayıcılarınızı yazmak aşırıya kaçacaktır. Ayrıca, G/Ç cihazları vb. İle çalışmanız gerekir. Ve donanım tarafından bazı destek gerektirecektir, böylece zamanlayıcınızın çalışmasını sağlamak için kesintileri tetikleyebilirsiniz. Temel olarak her seferinde yeni bir işletim sistemi yazıyorsunuz.
Genel olarak diş açma, dişlerin G/Ç'yi beklediği veya uyuduğu durumlarda performansı artırabilir. Ayrıca, uzun görevler gerçekleştirirken yanıt veren arabirimler oluşturmanıza ve işlemleri durdurmanıza izin verir. Ayrıca, iplik geçirme gerçek çok çekirdekli işlemcilerde işleri iyileştirir.