Aslında, async / beklemek o kadar büyülü değil. Tüm konu oldukça geniştir ancak sorunuza hızlı ve eksiksiz bir cevap için başarabileceğimizi düşünüyorum.
Bir Windows Forms uygulamasında basit bir düğme tıklama olayını ele alalım:
public async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before awaiting");
await GetSomethingAsync();
Console.WriteLine("after awaiting");
}
Ben gidiyorum açıkça değil de ne olursa olsun hakkında konuşmak GetSomethingAsync
şimdilik geri dönüyor. Diyelim ki bu 2 saniye sonra tamamlanacak bir şey.
Geleneksel, eşzamansız olmayan bir dünyada, düğme tıklama etkinliği işleyiciniz şöyle görünür:
public void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before waiting");
DoSomethingThatTakes2Seconds();
Console.WriteLine("after waiting");
}
Formdaki düğmeyi tıklattığınızda, uygulama yaklaşık 2 saniye donmuş gibi görünürken, bu yöntemin tamamlanmasını bekleriz. Olan şey, temelde bir döngü olan "mesaj pompası" nın engellenmesidir.
Bu döngü sürekli olarak pencerelere "Fareyi hareket ettirmiş gibi bir şey yaptı mı, bir şeye tıkladı mı? ve sonra "bir şey" i işler. Bu döngü, kullanıcının "button1" i (veya Windows'tan gelen eşdeğer mesaj türünü) tıkladığı ve button1_Click
yukarıdaki yöntemimizi çağırdığı bir mesaj aldı . Bu yöntem geri dönünceye kadar, bu döngü beklemede kaldı. Bu işlem 2 saniye sürer ve bu sırada hiçbir ileti işlenmez.
Pencerelerle ilgili çoğu şey mesajlar kullanılarak yapılır, yani mesaj döngüsü mesajların pompalanmasını durdurursa, bir saniye bile, kullanıcı tarafından hızlı bir şekilde fark edilir. Örneğin, not defterini veya başka bir programı kendi programınızın üzerine taşırsanız ve sonra tekrar uzaklaştırırsanız, programınıza pencerenin şimdi aniden tekrar görünür hale gelen bölgesini gösteren bir boya telaşı gönderilir. Bu mesajları işleyen mesaj döngüsü bir şey bekliyor, engelleniyorsa, boyama yapılmaz.
Yani, ilk örnekte ise, async/await
yeni bir iş parçacığı oluşturmazsa, bunu nasıl yapar?
Ne olur, yönteminiz ikiye ayrılır. Bu, bu geniş konu türlerinden biridir, bu yüzden çok fazla ayrıntıya girmeyeceğim, ancak yöntemin bu iki şeye ayrıldığını söylemem yeterli:
await
Çağrısı da dahil olmak üzere tüm kodGetSomethingAsync
- Aşağıdaki tüm kodlar
await
İllüstrasyon:
code... code... code... await X(); ... code... code... code...
Rearranged:
code... code... code... var x = X(); await X; code... code... code...
^ ^ ^ ^
+---- portion 1 -------------------+ +---- portion 2 ------+
Temel olarak yöntem şöyle çalışır:
- Kadar her şeyi yürütür
await
İşini yapan GetSomethingAsync
yöntemi çağırır ve gelecekte 2 saniye tamamlayacak bir şey döndürür
Şimdiye kadar, mesaj döngüsünden çağrılan ana iş parçacığında gerçekleşen, button1_Click'e yapılan orijinal çağrı içerisindeyiz. Kodu açan await
çok zaman alırsa, kullanıcı arayüzü yine de donar. Örneğimizde çok fazla değil
Ne await
kelime, araya bazı akıllı derleyici büyü ile, yaptığı temelde gibi bir şey "Tamam, sen, ben sadece burada düğmesini tıklatın olay işleyicisi dönmek için gidiyorum biliyorum. Ne zaman bir şey olduğu gibi (ki biz' dir beklemek) tamamlamak için dolaşmak, bana bildirin çünkü hala yürütmek için bazı kod kaldı ".
Aslında SynchronizationContext sınıfının yapıldığını bilmesine izin verir, şu anda oynatılmakta olan gerçek senkronizasyon içeriğine bağlı olarak yürütme için sıraya girer. Windows Forms programında kullanılan bağlam sınıfı, ileti döngüsünün pompaladığı kuyruğu kullanarak bu kuyruğu kuyruğa alır.
Böylece, pencereyi hareket ettirme, yeniden boyutlandırma veya diğer düğmeleri tıklatma gibi iletileri pompalamaya devam etmekte serbest olan mesaj döngüsüne geri döner.
Kullanıcı için, kullanıcı arayüzü şimdi tekrar yanıt veriyor, diğer düğme tıklamalarını işliyor, yeniden boyutlandırıyor ve en önemlisi yeniden çiziyor , bu yüzden donmuş gibi görünmüyor.
- 2 saniye sonra, beklediğimiz şey tamamlanıyor ve şimdi olan şey (iyi, senkronizasyon bağlamı) mesaj döngüsünün baktığı kuyruğa bir mesaj yerleştiriyor ve "Hey, çalıştırmanız gerekir "ve bu kod bekledikten sonra tüm koddur .
- İleti döngüsü bu iletiye ulaştığında, temelde o yöntemi bıraktığı yerden hemen sonra yeniden girer
await
ve yöntemin geri kalanını yürütmeye devam eder. Bu kodun mesaj döngüsünden tekrar çağrıldığını unutmayın, bu nedenle bu kod async/await
düzgün bir şekilde kullanmadan uzun bir şey yaparsa , mesaj döngüsünü tekrar engeller
Birçok hareketli parçalar daha fazla bilgi için bazı bağlantılar işte burada başlık altında vardır, ben "bunu gerektiğinde" diyecektim, ama bu konu olduğunu oldukça geniş ve bilmek oldukça önemlidir olanlar hareketli parçaların bazıları . Her zaman asenkron / beklemenin hala sızdıran bir kavram olduğunu anlayacaksınız. Temeldeki sınırlamalardan ve sorunlardan bazıları hala çevre koduna sızıyor ve eğer yapmazlarsa, görünüşte iyi bir neden olmadığı için rastgele kırılan bir uygulamada hata ayıklamak zorunda kalırsınız.
Tamam, peki ya GetSomethingAsync
2 saniye içinde tamamlanacak bir iplik döndürürse? Evet, tabii ki oyunda yeni bir iş parçacığı var. Bu iplik, ancak, değil , çünkü bu yöntemin programcı asenkron kod uygulamak için bir iş parçacığı seçtik çünkü öyle, bu yöntemin zaman uyumsuz-lik. Hemen hemen tüm asenkron I / O yok bir iş parçacığı kullanmak, bunlar farklı şeyler kullanırlar. async/await
kendi başlarına yeni dişler açmazlar, fakat “beklediğimiz şeyler” dişler kullanılarak uygulanabilir.
.NET'te mutlaka kendi başlarına bir iş parçacığı döndürmek değil ama yine de senkronize olmayan birçok şey vardır:
- Web istekleri (ve zaman alan ağla ilgili diğer birçok şey)
- Zaman uyumsuz dosya okuma ve yazma
- Söz konusu sınıf / arabirim seçti yöntemler ise ve daha birçok, iyi bir işarettir
SomethingSomethingAsync
veya BeginSomething
ve EndSomething
bir var IAsyncResult
dahil.
Genellikle bu şeyler kaputun altında bir iplik kullanmaz.
Tamam, yani o "geniş konu" ların bazılarını mı istiyorsunuz?
Peki, Roslyn'i deneyin düğmemizi soralım :
Roslyn'i deneyin
Burada tam olarak üretilen sınıfta bağlantı yapmayacağım ama oldukça kanlı şeyler.