Eşzamansız programlama ve çoklu iş parçacığı arasındaki fark nedir?


234

Temelde aynı şey olduklarını düşündüm - görevleri işlemciler arasında bölen programlar (2+ işlemcisi olan makinelerde). Sonra okuyorum bu , hangi diyor ki:

Async yöntemlerinin, engellemeyen 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 asenkron yönteminin 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.

ve birisinin bunu benim için İngilizceye çevirip çeviremeyeceğini merak ediyorum. Eşzamansızlık (bu bir kelime mi?) Ve iş parçacığı arasında bir ayrım çiziyor ve eşzamansız görevleri olan ancak çok iş parçacıklı olmayan bir programa sahip olabileceğiniz anlamına geliyor.

Şimdi pg'deki örnek gibi asenkron görevler fikrini anlıyorum. 467. Jon Skeet'in C # Derinliği, Üçüncü Baskı

async void DisplayWebsiteLength ( object sender, EventArgs e )
{
    label.Text = "Fetching ...";
    using ( HttpClient client = new HttpClient() )
    {
        Task<string> task = client.GetStringAsync("http://csharpindepth.com");
        string text = await task;
        label.Text = text.Length.ToString();
    }
}

asyncAnahtar kelime aracı " Bu işlev, bu çağrıldığında, çağrı çağrılacak sonra tamamlama her şey için gerekli olduğu bir bağlamda denilen edilmeyecektir."

Başka bir deyişle, bir görevin ortasında yazmak

int x = 5; 
DisplayWebsiteLength();
double y = Math.Pow((double)x,2000.0);

, çünkü DisplayWebsiteLength()hiçbir ilgisi yoktur xveya y, DisplayWebsiteLength()"arka planda" yürütülmesine neden olur , gibi

                processor 1                |      processor 2
-------------------------------------------------------------------
int x = 5;                                 |  DisplayWebsiteLength()
double y = Math.Pow((double)x,2000.0);     |

Açıkçası bu aptalca bir örnek, ama doğru muyum yoksa tamamen kafam karıştı mı yoksa ne?

(Ayrıca, ben neden karıştı senderve eşimdiye yukarıdaki fonksiyonun gövdesinde kullanılmaz.)



senderve ebunun aslında bir olay işleyici olduğunu öne sürüyoruz - hemen hemen async voidarzu edilen tek yer . Büyük olasılıkla, bu bir düğme tıklaması veya benzeri bir şeyle çağrılır - sonuç, bu eylemin uygulamanın geri kalanına göre tamamen eşzamansız olarak gerçekleşmesidir. Ama yine de hepsi tek bir iş parçacığında - UI iş parçacığı (UI iş parçacığına geri arama gönderen bir IOCP iş parçacığında küçük bir zaman şeridi ile).
Luaan


3
DisplayWebsiteLengthKod örneği hakkında çok önemli bir not : HttpClientBir usingdeyimde kullanmamalısınız - Ağır bir yük altında, kod kullanılabilir soket sayısını tüketerek SocketException hatalarına neden olabilir. Uygunsuz Instantiation hakkında daha fazla bilgi .
Gan

1
@JakubLortz Makalenin gerçekte kim olduğunu bilmiyorum. Yeni başlayanlar için değil, iş parçacıkları, kesmeler, CPU ile ilgili şeyler vb. Hakkında iyi bilgi gerektirdiğinden, ileri düzey kullanıcılar için değil, çünkü hepsi zaten açıktır. Eminim kimsenin bununla ilgili ne anlama geldiğini anlamamasına yardımcı olmaz - çok yüksek soyutlama seviyesi.
Loreno

Yanıtlar:


589

Yanlış anlamanız son derece yaygın. Birçok kişiye çoklu iş parçacığı ve eşzamansızlığın aynı şey olduğu öğretilir, ancak değildir.

Bir benzetme genellikle yardımcı olur. Bir restoranda yemek yapıyorsun. Yumurta ve tost için bir emir gelir.

  • Senkron: yumurtaları pişirirsiniz, sonra tostu pişirirsiniz.
  • Asenkron, tek dişli: yumurtaları pişirmeye başlar ve bir zamanlayıcı ayarlarsınız. Tost pişirmeye başlar ve bir zamanlayıcı ayarlarsınız. İkisi de yemek yaparken mutfağı temizlersiniz. Zamanlayıcılar bittiğinde yumurtaları ısıdan ve tosttan tost makinesinden çıkarır ve servis yaparsınız.
  • Asenkron, çok iş parçacıklı: biri yumurta pişirmek, diğeri tost pişirmek için iki aşçı daha kiralarsınız. Artık aşçıları koordine etme probleminiz var, böylece kaynakları paylaşırken mutfakta birbirleriyle çatışmasınlar. Ve onlara ödemek zorundasın.

Şimdi çoklu iş parçacığının sadece bir tür eşzamansızlık olması mantıklı mı? Diş çekme işçilerle ilgilidir; zaman uyumsuz görevlerle ilgilidir . Çok iş parçacıklı iş akışlarında işçilere görev atarsınız. Eşzamansız tek iş parçacıklı iş akışlarında, bazı görevlerin diğerlerinin sonuçlarına bağlı olduğu bir görev grafiğiniz vardır; her görev tamamlandıkça, az önce tamamlanan görevin sonuçları göz önüne alındığında, çalıştırılabilecek bir sonraki görevi zamanlayan kodu çağırır. Ancak (umarız) tüm görevleri yerine getirmek için sadece bir işçi gerekir, görev başına bir işçi değil.

Birçok görevin işlemciye bağlı olmadığını fark etmeye yardımcı olacaktır. İşlemciye bağlı görevler için, işlemciler olduğu kadar çok sayıda işçi (iş parçacığı) işe almak, her bir işçiye bir görev atamak, her bir işçiye bir işlemci atamak ve her işlemcinin sonucu hesaplamaktan başka bir şey yapmamasını sağlamak mantıklıdır olabildiğince çabuk. Ancak bir işlemciyi beklemeyen görevler için bir işçi atamanıza gerek yoktur. Sadece mesajın sonucun ulaşılabilir olmasını bekler ve beklerken başka bir şey yaparsınız . Bu mesaj geldiğinde, tamamlanan görevin devam etmesini kontrol etmek için yapılacaklar listenizdeki bir sonraki şey olarak planlayabilirsiniz.

Öyleyse Jon'un örneğine daha ayrıntılı bakalım. Ne oluyor?

  • Birisi DisplayWebSiteLength öğesini çağırır. DSÖ? Umurumuzda değil.
  • Bir etiket ayarlar, bir istemci oluşturur ve istemciden bir şey almasını ister. İstemci, bir şey getirme görevini temsil eden bir nesne döndürür. Bu görev devam ediyor.
  • Başka bir iş parçacığında devam ediyor mu? Muhtemelen değil. Stephen'ın neden iplik olmadığı hakkındaki makalesini okuyun .
  • Şimdi görevi bekliyoruz. Ne oluyor? Görevin, oluşturduğumuz zaman arasında tamamlanıp tamamlanmadığını kontrol ederiz ve bekledik. Evet ise, sonucu alır ve çalışmaya devam ederiz. Diyelim ki tamamlanmadı. Bu yöntemin geri kalanını bu görevin devamı ve geri dönüşü olarak kaydediyoruz .
  • Şimdi kontrol arayana geri döndü. Bu ne işe yarıyor? Ne isterse.
  • Şimdi görevin tamamlandığını varsayalım. Bunu nasıl yaptı? Belki başka bir iş parçacığı üzerinde çalışıyordu, ya da belki geri döndüğümüz arayan mevcut iş parçacığı üzerinde çalışmasına izin verdi. Ne olursa olsun, şimdi tamamlanmış bir görevimiz var.
  • Tamamlanan görev, görevin devam etmesini sağlamak için doğru iş parçacığını (yine de tek iş parçacığı) sorar .
  • Kontrol hemen beklediğimiz noktada bıraktığımız yönteme geçer. Şimdi bir sonuç var , böylece textyöntemin geri kalanını atayabilir ve çalıştırabiliriz.

Tıpkı benim benzetmemdeki gibi. Birisi sizden bir belge ister. Belge için postayı gönderip başka işler yapmaya devam edersiniz. Postaya ulaştığında size sinyal gönderilir ve bunu hissettiğinizde, iş akışının geri kalanını yaparsınız - zarfı açın, teslimat ücretlerini ödeyin, ne olursa olsun. Bütün bunları sizin için yapabilmek için başka bir işçi çalıştırmanıza gerek yoktur.


8
@ user5648283: Donanım, görevler hakkında düşünmek için yanlış düzeydedir. Görev, (1) değerinin gelecekte kullanılabilir olacağını ve (2) bu değer kullanılabilir olduğunda kodu (doğru iş parçacığında) çalıştırabileceğini temsil eden bir nesnedir . Gelecekte herhangi bir görevin sonucu nasıl elde edeceği ona bağlıdır. Bazıları bunu yapmak için "diskler" ve "ağ kartları" gibi özel donanımlar kullanır; bazıları CPU gibi donanımlar kullanır.
Eric Lippert

13
@ user5648283: Yine, benzetmem hakkında düşün. Birisi sizden yumurta ve tost pişirmenizi istediğinde, özel bir donanım kullanırsınız - bir ocak ve bir tost makinesi - ve donanım işini yaparken mutfağı temizleyebilirsiniz. Birisi sizden yumurta, tost ve son Hobbit filminin orijinal bir eleştirisini isterse, yumurta ve tost pişirilirken yorumunuzu yazabilirsiniz, ancak bunun için donanım kullanmanız gerekmez.
Eric Lippert

9
@ user5648283: Şimdi "kodu yeniden düzenleme" hakkındaki sorunuz için bunu göz önünde bulundurun. Bir verim geri dönüşüne sahip bir P yönteminiz ve P'nin sonucu üzerinde bir öngörü yapan bir Q yönteminiz olduğunu varsayalım. Biraz Q, sonra biraz P sonra biraz Q çalıştırdığımızı göreceksiniz ... Bunun amacını anlıyor musunuz? beklemek esas olarak fantezi elbise getiri dönüşüdür . Şimdi daha açık mı?
Eric Lippert

10
Ekmek kızartma makinesi donanımdır. Donanımın servis için bir ipliğe ihtiyacı yoktur; diskler ve ağ kartları ve işletim sistemi iş parçacıklarının çok altında bir seviyede çalışmaz.
Eric Lippert

5
@ShivprasadKoirala: Bu kesinlikle doğru değil . Buna inanıyorsanız, eşzamansızlık hakkında çok yanlış inançlarınız var . C # asenkronisi bütün mesele bu mu yani değil bir iş parçacığı oluşturun.
Eric Lippert

27

Tarayıcı İçi Javascript, iş parçacığı olmayan eşzamansız programa harika bir örnektir.

Aynı nesnelere aynı anda dokunan birden fazla kod parçası için endişelenmenize gerek yoktur: sayfada herhangi bir başka javascript çalışmasına izin verilmeden önce her işlev çalışmayı bitirecektir.

Ancak, AJAX isteği gibi bir şey yaparken, hiçbir kod çalışmaz, bu nedenle diğer javascript, bu istek geri gelene ve onunla ilişkili geri çağrıyı çağırana kadar tıklama olayları gibi şeylere yanıt verebilir. AJAX isteği geri döndüğünde bu diğer olay işleyicilerinden biri hala çalışıyorsa, işleyicileri tamamlanana kadar çağrılmaz. Gereksinim duyduğunuz bilgilere sahip olana kadar yaptığınız şeyi etkili bir şekilde duraklatmanız mümkün olsa da, yalnızca bir JavaScript "iş parçacığı" çalışıyor.

C # uygulamalarında, UI öğeleriyle her uğraştığınızda aynı şey olur - yalnızca UI iş parçacığında olduğunuzda UI öğeleriyle etkileşime girmenize izin verilir. Kullanıcı bir düğmeyi tıkladığında ve diskten büyük bir dosyayı okuyarak yanıt vermek istiyorsanız, deneyimsiz bir programcı, tıklama olayı işleyicisinin içindeki dosyayı okuma hatasını yapabilir ve bu da uygulamanın "donmasına" neden olur. iş parçacığı serbest kalana kadar daha fazla tıklama, fareyle üzerine gelme veya kullanıcı arayüzüyle ilgili diğer etkinliklere yanıt vermesine izin verilmediğinden dosya yüklemeyi tamamladı.

Programcıların bu sorunu önlemek için kullanabileceği seçeneklerden biri, dosyayı yüklemek için yeni bir iş parçacığı oluşturmak ve daha sonra, iş parçacığının kodunu dosya yüklendiğinde, kullanıcı arabirimi öğelerini güncelleştirebilmesi için UI iş parçacığında kalan kodu yeniden çalıştırması gerektiğini belirtmektir. dosyada bulduklarına göre. Yakın zamana kadar, bu yaklaşım çok popülerdi, çünkü C # kütüphaneleri ve dil kolaylaşıyordu, ancak temelde olması gerekenden daha karmaşıktı.

CPU'nun bir dosyayı donanım ve İşletim Sistemi düzeyinde okuduğunda ne yaptığını düşünüyorsanız, temel olarak veri parçalarını diskten belleğe okumak ve işletim sistemine "kesintiyle" vurmak için bir talimat verir. "okuma işlemi tamamlandığında. Başka bir deyişle, diskten (veya gerçekten herhangi bir G / Ç) okumak doğal olarak eşzamansız bir işlemdir. Bu G / Ç'nin tamamlanmasını bekleyen bir iş parçacığı kavramı, kütüphane geliştiricilerinin programlamayı kolaylaştırmak için oluşturduğu bir soyutlamadır. Gereksiz.

Şimdi, .NET'teki çoğu G / Ç işlemi ...Async()çağırabileceğiniz karşılık gelen bir yönteme sahiptir, bu Taskhemen hemen geri döner . TaskEşzamansız işlem tamamlandığında çalıştırılmasını istediğiniz kodu belirtmek için buna geri çağrılar ekleyebilirsiniz . Ayrıca, bu kodun hangi iş parçacığında çalışmasını istediğinizi belirtebilir ve eşzamansız işlemin zaman zaman kontrol edebileceği bir belirteci sunarak eşzamansız görevi iptal edip etmeyeceğinize karar vererek çalışmasını hızlı bir şekilde durdurma fırsatı verebilirsiniz. ve incelikle.

Kadar async/awaitanahtar kelime eklenmedi o geri çağrılar görevle ilişkili olduğu delege şeklinde oldukları için, C #, geri arama kodu çağrılan nasıl çok daha açıktı. ...Async()Operasyondan yararlanmanın avantajını sağlamak için, koddaki karmaşıklıktan kaçınırken async/await, bu delegelerin oluşturulmasını ortadan kaldırır . Ama hala derlenmiş koddalar.

Böylece, UI olay işleyicinizin awaitbir G / Ç işlemi gerçekleştirmesini, UI iş parçacığını başka şeyler yapmak için serbest bırakmasını ve dosyayı okumayı bitirdiğinizde daha fazla veya daha az otomatik olarak UI iş parçacığına geri dönmesini sağlayabilirsiniz. yeni bir evre yarat.


Çalışan yalnızca bir JavaScript "iş parçacığı" var - artık Web Çalışanları için geçerli değil .
x

6
@oleksii: Bu teknik olarak doğru, ancak Web Workers API'sının kendisi eşzamansız olduğu ve Web Workers'ın çağrılan web sayfasında javascript değerlerini veya DOM'u doğrudan etkilemesine izin verilmediğinden buna girmeyecektim. dan, yani bu cevabın önemli ikinci paragrafı hala geçerlidir. Programcının bakış açısından, bir Web Çalışanı'nı çağırmakla AJAX isteğini çağırmak arasında çok az fark vardır.
StriplingWarrior
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.