Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Görevi zaman uyumsuz programlama (TAP) modeli tipik zaman uyumsuz kodlamaya göre bir soyutlama katmanı sağlar. Bu modelde, kodu her zamanki gibi bir deyim dizisi olarak yazarsınız. Aradaki fark, derleyici her deyimi işlerken ve sonraki deyimi işlemeye başlamadan önce görev tabanlı kodunuzu okuyabilmenizdir. Bu modeli gerçekleştirmek için, derleyici her görevi tamamlamak için birçok dönüştürme gerçekleştirir. Bazı deyimler çalışmayı başlatabilir ve devam eden çalışmayı temsil eden bir Task nesnesi döndürebilir ve derleyicinin bu dönüştürmeleri çözümlemesi gerekir. Görev zaman uyumsuz programlamanın amacı, bir dizi ifade gibi okunan ancak daha karmaşık bir sırada yürütülen kodlar oluşturmayı sağlamaktır. Yürütme, dış kaynak ayırmayı ve görevlerin ne zaman tamamlanmasını temel alır.
Zaman uyumsuz görev programlama modeli, kişilerin zaman uyumsuz görevler içeren işlemler için yönergeler vermesine benzer. Bu makalede, Async ve Await anahtar sözcüklerinin bir dizi zaman uyumsuz yönerge içeren kod hakkında daha kolay mantık yürütmeyi nasıl kolaylaştırdığını göstermek için kahvaltı yapma yönergelerini içeren bir örnek kullanılmaktadır. Kahvaltı yapma yönergeleri liste olarak sağlanabilir:
- Bir fincan kahve koyun.
- Bir tava ısıtın, sonra iki yumurta kızartın.
- Üç tane hash brown pişirin.
- İki dilim ekmek kızartın.
- Tost üzerine tereyağı ve reçel sürün.
- Bir bardak portakal suyu dökün.
Yemek pişirme konusunda deneyimliyseniz, bu yönergeleri asenkron olaraktamamlayabilirsiniz. Tavayı yumurtalar için ısıtmaya başlarsınız, sonra patates kroketlerini pişirmeye başlarsınız. Ekmeği tost makinesine koyarsın, sonra yumurtaları pişirmeye başlarsın. İşlemin her adımında bir görev başlatırsınız ve sonra dikkatinize hazır olan diğer görevlere geçersiniz.
Kahvaltı pişirmek, paralel olmayan zaman uyumsuz çalışmaların iyi bir örneğidir. Bir kişi (ya da iş parçacığı) tüm görevleri işleyebilir. Bir kişi, önceki görev tamamlanmadan bir sonraki görevi başlatarak zaman uyumsuz olarak kahvaltı yapabilir. Her pişirme görevi, birinin işlemi etkin olarak izleyip izlemediğine bakılmaksızın ilerler. Yumurtalar için tavayı ısıtmaya başladığınızda, patates pankeklerini pişirmeye başlayabilirsiniz. Rendelenmiş patates kızartması pişmeye başladıktan sonra ekmeği tost makinesine koyabilirsiniz.
Paralel algoritma için, yemek pişiren birden çok kişiye (veya birden çok iş parçacığına) ihtiyacınız vardır. Bir kişi yumurtaları pişirir, diğeri patates kızartmasını pişirir, ve benzeri. Her kişi kendi belirli bir görevine odaklanır. Yemek pişiren (veya her iş parçacığı) geçerli görevin tamamlanmasını bekleyen her kişi zaman uyumlu olarak engellenir: Patates mücveri çevrilmeye hazır, ekmek kızartma makinesinden fırlar ve benzeri.
30 dakikada tamamlanan yedi sıralı görev listesi olarak kahvaltı hazırlama yönergelerini gösteren
Visual Basic kodu ifadeleri olarak yazılmış senkron yönergelerin aynı listesini göz önünde bulundurun.
Sub Main()
Dim cup As Coffee = PourCoffee()
Console.WriteLine("coffee is ready")
Dim eggs As Egg = FryEggs(2)
Console.WriteLine("eggs are ready")
Dim hashBrown As HashBrown = FryHashBrowns(3)
Console.WriteLine("hash browns are ready")
Dim toast As Toast = ToastBread(2)
ApplyButter(toast)
ApplyJam(toast)
Console.WriteLine("toast is ready")
Dim oj As Juice = PourOJ()
Console.WriteLine("oj is ready")
Console.WriteLine("Breakfast is ready!")
End Sub
Private Function PourOJ() As Juice
Console.WriteLine("Pouring orange juice")
Return New Juice()
End Function
Private Sub ApplyJam(toast As Toast)
Console.WriteLine("Putting jam on the toast")
End Sub
Private Sub ApplyButter(toast As Toast)
Console.WriteLine("Putting butter on the toast")
End Sub
Private Function ToastBread(slices As Integer) As Toast
For slice As Integer = 0 To slices - 1
Console.WriteLine("Putting a slice of bread in the toaster")
Next
Console.WriteLine("Start toasting...")
Task.Delay(3000).Wait()
Console.WriteLine("Remove toast from toaster")
Return New Toast()
End Function
Private Function FryHashBrowns(patties As Integer) As HashBrown
Console.WriteLine($"putting {patties} hash brown patties in the pan")
Console.WriteLine("cooking first side of hash browns...")
Task.Delay(3000).Wait()
For patty As Integer = 0 To patties - 1
Console.WriteLine("flipping a hash brown patty")
Next
Console.WriteLine("cooking the second side of hash browns...")
Task.Delay(3000).Wait()
Console.WriteLine("Put hash browns on plate")
Return New HashBrown()
End Function
Private Function FryEggs(howMany As Integer) As Egg
Console.WriteLine("Warming the egg pan...")
Task.Delay(3000).Wait()
Console.WriteLine($"cracking {howMany} eggs")
Console.WriteLine("cooking the eggs ...")
Task.Delay(3000).Wait()
Console.WriteLine("Put eggs on plate")
Return New Egg()
End Function
Private Function PourCoffee() As Coffee
Console.WriteLine("Pouring coffee")
Return New Coffee()
End Function
Bu yönergeleri bir bilgisayar gibi yorumlarsanız kahvaltının hazırlanması yaklaşık 30 dakika sürer. Süre, tek tek görev sürelerinin toplamıdır. Bilgisayar, tüm çalışmalar tamamlanana kadar her deyimi engeller ve sonraki görev deyimine devam eder. Bu yaklaşım önemli zaman alabilir. Kahvaltı örneğinde bilgisayar yöntemi tatmin edici olmayan bir kahvaltı oluşturur. Eşzamanlı listede yer alan, ekmek kızartmak gibi sonraki görevler, önceki görevler tamamlanana kadar başlamaz. Kahvaltı servise hazır olmadan önce bazı yiyecekler soğur.
Bilgisayarın yönergeleri zaman uyumsuz olarak yürütmesini istiyorsanız, zaman uyumsuz kod yazmanız gerekir. İstemci programları yazarken, kullanıcı arabiriminin kullanıcı girişine yanıt vermesini istersiniz. Uygulamanız web'den veri indirirken tüm etkileşimi dondurmamalıdır. Sunucu programları yazarken, diğer isteklere hizmet ediyor olabilecek iş parçacıklarını engellemek istemezsiniz. Zaman uyumsuz alternatifler mevcut olduğunda zaman uyumlu kod kullanmak, ölçeği daha az maliyetli bir şekilde genişletmenize zarar verir. Engellenen iş parçacıkları için ödeme yapabilirsiniz.
Başarılı modern uygulamalar zaman uyumsuz kod gerektirir. Dil desteği olmadan, zaman uyumsuz kod yazmak için geri çağırmalar, tamamlama olayları veya kodun özgün amacını gizleyen başka bir araç gerekir. Zaman uyumlu kodun avantajı, taramayı ve anlamayı kolaylaştıran adım adım eylemdir. Geleneksel zaman uyumsuz modeller, kodun temel eylemlerine değil, kodun zaman uyumsuz doğasına odaklanmanızı zorlar.
Engellemeyin, onun yerine bekleyin
Önceki kod, talihsiz bir programlama uygulamasını vurgular: Asenkron işlemler gerçekleştirmek için senkron kod yazma. Kod, mevcut iş parçacığının başka bir iş yapmasını engeller. Çalışan görevler varken kod iş parçacığını kesintiye uğratmaz. Bu modelin sonucu, ekmeği koyduktan sonra tost makinesine bakmaya benzer. Kesintileri yoksayarsınız ve ekmek açılana kadar diğer görevleri başlatmazsınız. Tereyağını ve reçeli buzdolabından çıkaramazsın. Ocakta başlayan bir yangını görmeyi kaçırabilirsiniz. Hem ekmeği kızartmak hem de diğer işleri/sorunları aynı anda halletmek istiyorsunuz. Aynı durum kodunuz için de geçerlidir.
İş parçacığının görevler yürütülürken engellenmemesi için kodu güncelleyerek işe başlayabilirsiniz.
Await anahtar sözcüğü, bir görevi başlatmak ve görev tamamlandığında yürütmeye devam etmek için engelleyici olmayan bir yol sağlar. Kahvaltı kodunun basit bir zaman uyumsuz sürümü aşağıdaki kod parçacığına benzer:
Module AsyncBreakfastProgram
Async Function Main() As Task
Dim cup As Coffee = PourCoffee()
Console.WriteLine("coffee is ready")
Dim eggs As Egg = Await FryEggsAsync(2)
Console.WriteLine("eggs are ready")
Dim hashBrown As HashBrown = Await FryHashBrownsAsync(3)
Console.WriteLine("hash browns are ready")
Dim toast As Toast = Await ToastBreadAsync(2)
ApplyButter(toast)
ApplyJam(toast)
Console.WriteLine("toast is ready")
Dim oj As Juice = PourOJ()
Console.WriteLine("oj is ready")
Console.WriteLine("Breakfast is ready!")
End Function
Private Async Function ToastBreadAsync(slices As Integer) As Task(Of Toast)
For slice As Integer = 0 To slices - 1
Console.WriteLine("Putting a slice of bread in the toaster")
Next
Console.WriteLine("Start toasting...")
Await Task.Delay(3000)
Console.WriteLine("Remove toast from toaster")
Return New Toast()
End Function
Private Async Function FryHashBrownsAsync(patties As Integer) As Task(Of HashBrown)
Console.WriteLine($"putting {patties} hash brown patties in the pan")
Console.WriteLine("cooking first side of hash browns...")
Await Task.Delay(3000)
For patty As Integer = 0 To patties - 1
Console.WriteLine("flipping a hash brown patty")
Next
Console.WriteLine("cooking the second side of hash browns...")
Await Task.Delay(3000)
Console.WriteLine("Put hash browns on plate")
Return New HashBrown()
End Function
Private Async Function FryEggsAsync(howMany As Integer) As Task(Of Egg)
Console.WriteLine("Warming the egg pan...")
Await Task.Delay(3000)
Console.WriteLine($"cracking {howMany} eggs")
Console.WriteLine("cooking the eggs ...")
Await Task.Delay(3000)
Console.WriteLine("Put eggs on plate")
Return New Egg()
End Function
Private Function PourCoffee() As Coffee
Console.WriteLine("Pouring coffee")
Return New Coffee()
End Function
Private Function PourOJ() As Juice
Console.WriteLine("Pouring orange juice")
Return New Juice()
End Function
Private Sub ApplyJam(toast As Toast)
Console.WriteLine("Putting jam on the toast")
End Sub
Private Sub ApplyButter(toast As Toast)
Console.WriteLine("Putting butter on the toast")
End Sub
End Module
Kod, FryEggs, FryHashBrowns ve ToastBread özgün yöntem gövdelerini sırasıyla Task(Of Egg), Task(Of HashBrown) ve Task(Of Toast) nesnelerini döndürecek şekilde günceller. Güncellenmiş yöntem adları "Async" son ekini içerir: FryEggsAsync, FryHashBrownsAsync, ve ToastBreadAsync.
Main İşlev, tasarım gereği bir Return ifadesi olmamasına rağmen Task nesnesini döndürür.
Uyarı
Güncelleştirilmiş kod henüz zaman uyumsuz programlamanın temel özelliklerinden yararlanmaz ve bu da daha kısa tamamlanma sürelerine neden olabilir. Kod, görevleri kabaca ilk zaman uyumlu sürümle aynı süre içinde işler. Tam yöntem uygulamaları için bu makalenin devamında kodun son sürümüne bakın.
Güncelleştirilmiş koda kahvaltı örneğini uygulayalım. Yumurtalar veya patates mücveri pişirilirken iş parçacığı bloke olmaz, ancak mevcut işlem tamamlanana kadar kod diğer görevleri başlamaz. Ekmekleri hâlâ ekmek kızartma makinesine koyar ve ekmekler fırlayana kadar ekmek kızartma makinesine bakarsınız, ancak artık kesintilere yanıt verebilirsiniz. Birden çok siparişin verildiği bir restoranda, başka bir yemek pişirirken aşçı yeni bir sipariş başlatabilir.
Güncellenmiş kodda, kahvaltı hazırlığında çalışan iş parçacığı, başlatılan herhangi bir tamamlanmamış görev beklenirken engellenmez. Bazı uygulamalarda ihtiyacınız olan tek şey bu değişikliktir. Web'den veri indirilirken uygulamanızın kullanıcı etkileşimini desteklemesini sağlayabilirsiniz. Diğer senaryolarda, önceki görevin tamamlanmasını beklerken diğer görevleri başlatmak isteyebilirsiniz.
Görevleri eşzamanlı olarak başlatma
Çoğu işlem için birkaç bağımsız görevi hemen başlatmak istiyorsunuz. Her görev tamamlandıktan sonra başlamaya hazır olan diğer işleri başlatırsınız. Bu metodolojiyi kahvaltı örneğine uyguladığınızda kahvaltıyı daha hızlı hazırlayabilirsiniz. Ayrıca her şeyi aynı zamana yakın hazırlarsınız, böylece sıcak kahvaltının tadını çıkarabilirsiniz.
Task sınıfı ve ilgili türler, devam eden görevlere bu mantık stilini uygulamak için kullanabileceğiniz sınıflardır. Bu yaklaşım, gerçek hayatta kahvaltı oluşturma şeklinize daha yakın bir kod yazmanızı sağlar. Yumurta, patates ve tost pişirmeye aynı anda başlarsınız. Her yiyecek öğesi eylem gerektirdiğinden, dikkatinizi bu göreve çevirir, eylemle ilgilenir ve sonra dikkatinizi gerektiren başka bir şey beklersiniz.
Kodunuzda bir görev başlatır ve çalışmayı temsil eden Task nesnesini tutarsınız. Sonuç hazır olana kadar işi ertelemek için görevdeki Await yöntemini kullanırsınız.
Bu değişiklikleri kahvaltı koduna uygulayın. İlk adım, Await ifadesini kullanmak yerine, işlemler başladığında görevleri depolamaktır:
Dim cup As Coffee = PourCoffee()
Console.WriteLine("Coffee is ready")
Dim eggsTask As Task(Of Egg) = FryEggsAsync(2)
Dim eggs As Egg = Await eggsTask
Console.WriteLine("Eggs are ready")
Dim hashBrownTask As Task(Of HashBrown) = FryHashBrownsAsync(3)
Dim hashBrown As HashBrown = Await hashBrownTask
Console.WriteLine("Hash browns are ready")
Dim toastTask As Task(Of Toast) = ToastBreadAsync(2)
Dim toast As Toast = Await toastTask
ApplyButter(toast)
ApplyJam(toast)
Console.WriteLine("Toast is ready")
Dim oj As Juice = PourOJ()
Console.WriteLine("Oj is ready")
Console.WriteLine("Breakfast is ready!")
Bu düzeltmeler kahvaltınızı daha hızlı hazırlamanıza yardımcı olmaz.
Await ifadesi, başlar başlamaz tüm görevlere uygulanır. Bir sonraki adım, Await patates kızartması ve yumurtalar için ifadeleri, kahvaltıyı sunmadan önce yöntemin sonuna taşımaktır.
Dim cup As Coffee = PourCoffee()
Console.WriteLine("Coffee is ready")
Dim eggsTask As Task(Of Egg) = FryEggsAsync(2)
Dim hashBrownTask As Task(Of HashBrown) = FryHashBrownsAsync(3)
Dim toastTask As Task(Of Toast) = ToastBreadAsync(2)
Dim toast As Toast = Await toastTask
ApplyButter(toast)
ApplyJam(toast)
Console.WriteLine("Toast is ready")
Dim oj As Juice = PourOJ()
Console.WriteLine("Oj is ready")
Dim eggs As Egg = Await eggsTask
Console.WriteLine("Eggs are ready")
Dim hashBrown As HashBrown = Await hashBrownTask
Console.WriteLine("Hash browns are ready")
Console.WriteLine("Breakfast is ready!")
Artık tüm süreyi adamak zorunda kalmadan yaklaşık 20 dakikada hazırlayabileceğiniz bir kahvaltınız var. Bazı görevler aynı anda çalıştığından toplam pişirme süresi azalır.
Kod güncelleştirmeleri, pişirme süresini azaltarak hazırlık sürecini geliştirir, ancak yumurtaları ve patates mücverini yakarak bir gerilemeye neden olabilir. Tüm zaman uyumsuz görevleri aynı anda başlatırsınız. Her görevde yalnızca sonuçlara ihtiyacınız olduğunda beklersiniz. Kod, farklı mikro hizmetlere istekte bulunan ve ardından sonuçları tek bir sayfada birleştiren bir web uygulamasındaki programa benzer olabilir. Tüm istekleri hemen yapar ve ardından tüm bu görevlere Await ifadesini uygular ve web sayfasını oluşturursunuz.
Görevlerle destek yapılandırma
Önceki kod düzeltmeleri, tost dışında kahvaltı için her şeyi aynı anda hazırlamaya yardımcı olur. Tost yapma süreci, asenkron bir işlem (ekmeği kızartmak) ile senkron işlemler (tostun üzerine tereyağı ve reçel sürmek) bir bileşimdir. Bu örnekte, zaman uyumsuz programlama hakkında önemli bir kavram gösterilmektedir:
Önemli
Zaman uyumsuz bir işlemin ve ardından zaman uyumlu çalışmanın bileşimi zaman uyumsuz bir işlemdir. Başka bir şekilde ifade etmek gerekirse, bir işlemin herhangi bir bölümü zaman uyumsuzsa, işlemin tamamı zaman uyumsuzdur.
Önceki güncelleştirmelerde, çalışan görevleri tutmak için Task veya Task<TResult> nesneleri kullanmayı öğrendinsiniz. Her görevin sonucunu kullanmadan önce beklemeniz gerekir. Sonraki adım, diğer çalışmaların birleşimini temsil eden yöntemler oluşturmaktır. Kahvaltı sunmadan önce, tereyağı ve reçeli yaymadan önce ekmeğin kızartılmasını temsil eden görevi beklemek istiyorsunuz.
Bu çalışmayı aşağıdaki kodla temsil edebilirsiniz:
Async Function MakeToastWithButterAndJamAsync(number As Integer) As Task(Of Toast)
Dim toast As Toast = Await ToastBreadAsync(number)
ApplyButter(toast)
ApplyJam(toast)
Return toast
End Function
MakeToastWithButterAndJamAsync yöntemi, derleyiciye yöntemin bir Async ifadesi içerdiğini ve zaman uyumsuz işlemler içerdiğini belirten Await değiştiricisine sahiptir. Metod, önce ekmeği kızartır, sonra tereyağını ve reçeli yayar görevi temsil eder. yöntemi, üç işlemin bileşimini temsil eden bir Task<TResult> nesnesi döndürür.
Düzeltilen ana kod bloğu şu şekilde görünür:
Async Function Main() As Task
Dim cup As Coffee = PourCoffee()
Console.WriteLine("coffee is ready")
Dim eggsTask = FryEggsAsync(2)
Dim hashBrownTask = FryHashBrownsAsync(3)
Dim toastTask = MakeToastWithButterAndJamAsync(2)
Dim eggs = Await eggsTask
Console.WriteLine("eggs are ready")
Dim hashBrown = Await hashBrownTask
Console.WriteLine("hash browns are ready")
Dim toast = Await toastTask
Console.WriteLine("toast is ready")
Dim oj As Juice = PourOJ()
Console.WriteLine("oj is ready")
Console.WriteLine("Breakfast is ready!")
End Function
Bu kod değişikliği, zaman uyumsuz kodla çalışmaya yönelik önemli bir tekniği gösterir. İşlemleri bir görev döndüren yeni bir yönteme ayırarak görevleri oluşturursunuz. Bu görevde ne zaman bekleyebileceğinizi seçebilirsiniz. Diğer görevleri eşzamanlı olarak başlatabilirsiniz.
Zaman uyumsuz özel durumları işleme
Bu noktaya kadar kodunuz tüm görevlerin başarıyla tamamlanmasını örtük olarak varsayar. Zaman uyumsuz yöntemler, zaman uyumlu karşılıkları gibi istisnalar fırlatır. Özel durumlar ve hata işleme için zaman uyumsuz desteğin hedefleri genel olarak zaman uyumsuz destekle aynıdır. En iyi uygulama, senkron ifadeler dizisi gibi okunan kod yazmaktır. Görevler başarıyla tamamlanamadıklarında istisnalar oluşturur. İstemci kodu, Await ifadesi başlatılan göreve uygulandığında bu özel durumları yakalayabilir.
Kahvaltı örneğinde, ekmek kızartırken tost makinesinin alev aldığını varsayalım.
ToastBreadAsync yöntemini aşağıdaki kodla eşleşecek şekilde değiştirerek bu sorunun benzetimini yapabilirsiniz:
Private Async Function ToastBreadAsync(slices As Integer) As Task(Of Toast)
For slice As Integer = 0 To slices - 1
Console.WriteLine("Putting a slice of bread in the toaster")
Next
Console.WriteLine("Start toasting...")
Await Task.Delay(2000)
Console.WriteLine("Fire! Toast is ruined!")
Throw New InvalidOperationException("The toaster is on fire")
Await Task.Delay(1000)
Console.WriteLine("Remove toast from toaster")
Return New Toast()
End Function
Uyarı
Bu kodu derlerken ulaşılamayan kodla ilgili bir uyarı görürsünüz. Bu hata tasarım gereğidir. Tost makinesi yandıktan sonra işlemler normal şekilde devam etmediğinden kod bir hata döndürür.
Kod değişikliklerini yaptıktan sonra uygulamayı çalıştırın ve çıkışı denetleyin:
Pouring coffee
Coffee is ready
Warming the egg pan...
putting 3 hash brown patties in the pan
Cooking first side of hash browns...
Putting a slice of bread in the toaster
Putting a slice of bread in the toaster
Start toasting...
Fire! Toast is ruined!
Flipping a hash brown patty
Flipping a hash brown patty
Flipping a hash brown patty
Cooking the second side of hash browns...
Cracking 2 eggs
Cooking the eggs ...
Put hash browns on plate
Put eggs on plate
Eggs are ready
Hash browns are ready
Unhandled exception. System.InvalidOperationException: The toaster is on fire
at AsyncBreakfast.Program.ToastBreadAsync(Int32 slices) in Program.vb:line 65
at AsyncBreakfast.Program.MakeToastWithButterAndJamAsync(Int32 number) in Program.vb:line 36
at AsyncBreakfast.Program.Main(String[] args) in Program.vb:line 24
at AsyncBreakfast.Program.<Main>(String[] args)
Tost makinesinin alev aldığı ve sistemin özel durumu gözlemlediği zaman arasında oldukça az sayıda görev tamamlandığını unutmayın. Zaman uyumsuz olarak çalıştırılan bir görev özel durum oluşturursa, bu görev hatalı.
Task nesnesi, Task.Exception özelliğinde fırlatılan istisnayı barındırıyor. Hataya neden olan görevler, ifade göreve uygulandığında özel durum Await.
Bu işlem hakkında anlaşılması gereken iki önemli mekanizma vardır:
- Bir istisnanın hatalı bir görevde nasıl saklandığı.
- Kod hatalı bir görevde (
Await) beklerken özel durumun nasıl paketten çıkarıldığı ve tekrar atıldığı.
Zaman uyumsuz olarak çalıştırılan kod bir özel durum oluşturursa, özel durum Task nesnesinde depolanır.
Task.Exception özelliği, zaman uyumsuz çalışma sırasında birden fazla istisna oluşturulabileceğinden bir AggregateException nesnesidir. Oluşan özel durumlar AggregateException.InnerExceptions koleksiyonuna eklenir.
Exception özelliği null ise, yeni bir AggregateException nesnesi oluşturulur ve oluşturulan özel durum koleksiyondaki ilk öğedir.
Hatalı bir görevin en yaygın senaryosu, Exception özelliğinin tam olarak bir özel durum içermesidir. Kodunuz hata veren bir görevde beklediğinde, koleksiyondaki ilk AggregateException.InnerExceptions özel durumunu yeniden fırlatır. Bu sonuç, örnekteki çıktının InvalidOperationException nesnesi yerine AggregateException nesnesini göstermesinin nedenidir. İlk iç özel durumun ayıklanması, zaman uyumsuz yöntemlerle çalışmayı, zaman uyumlu karşılıklarıyla çalışmaya mümkün olduğunca benzer hale getirir. Senaryonuz birden çok özel durum oluşturabileceğinden kodunuzda Exception özelliğini inceleyebilirsiniz.
Tavsiye
Önerilen uygulama, görev döndüren yöntemlerden zaman uyumlu bağımsız değişken doğrulama özel durumlarının ortaya çıkmasıdır. Daha fazla bilgi ve örnek için bkz. görev döndüren yöntemlerdeki özel durumlar.
Sonraki bölüme geçmeden önce, ToastBreadAsync yönteminizdeki aşağıdaki iki deyimi yorum satırı haline getirin. Bir yangın daha başlatmak istemezsiniz:
' Console.WriteLine("Fire! Toast is ruined!")
' Throw New InvalidOperationException("The toaster is on fire")
Await ifadelerini görevlere verimli bir şekilde uygulayın
Await sınıfının yöntemlerini kullanarak önceki kodun sonundaki Task ifade serisini geliştirebilirsiniz. API'lerden biri, bağımsız değişken listesindeki tüm görevler tamamlandığında tamamlanan bir WhenAll nesnesi döndüren Task yöntemidir. Aşağıdaki kod bu yöntemi gösterir:
Await Task.WhenAll(eggsTask, hashBrownTask, toastTask)
Console.WriteLine("Eggs are ready")
Console.WriteLine("Hash browns are ready")
Console.WriteLine("Toast is ready")
Console.WriteLine("Breakfast is ready!")
Diğer bir seçenek, bağımsız değişkenlerinden herhangi biri tamamlandığında tamamlanan bir WhenAny nesnesi döndüren Task(Of Task) yöntemini kullanmaktır. Görevin tamamlanmasını bildiğiniz için döndürülen görevde bekleyebilirsiniz. Aşağıdaki kodda, ilk görevin tamamlanmasını beklemek ve ardından sonucunu işlemek için WhenAny yöntemini nasıl kullanabileceğiniz gösterilmektedir. Tamamlanan görevden sonucu işledikten sonra, tamamlanmış görevi WhenAny yöntemine geçirilen görevler listesinden kaldırırsınız.
Module ConcurrentBreakfastProgram
Async Function Main() As Task
Dim cup As Coffee = PourCoffee()
Console.WriteLine("Coffee is ready")
Dim eggsTask As Task(Of Egg) = FryEggsAsync(2)
Dim hashBrownTask As Task(Of HashBrown) = FryHashBrownsAsync(3)
Dim toastTask As Task(Of Toast) = MakeToastWithButterAndJamAsync(2)
Dim breakfastTasks As New List(Of Task) From {eggsTask, hashBrownTask, toastTask}
While breakfastTasks.Count > 0
Dim finishedTask As Task = Await Task.WhenAny(breakfastTasks)
If finishedTask Is eggsTask Then
Console.WriteLine("eggs are ready")
ElseIf finishedTask Is hashBrownTask Then
Console.WriteLine("hash browns are ready")
ElseIf finishedTask Is toastTask Then
Console.WriteLine("toast is ready")
End If
Await finishedTask
breakfastTasks.Remove(finishedTask)
End While
Dim oj As Juice = PourOJ()
Console.WriteLine("oj is ready")
Console.WriteLine("Breakfast is ready!")
End Function
Async Function MakeToastWithButterAndJamAsync(number As Integer) As Task(Of Toast)
Dim toast As Toast = Await ToastBreadAsync(number)
ApplyButter(toast)
ApplyJam(toast)
Return toast
End Function
Private Async Function ToastBreadAsync(slices As Integer) As Task(Of Toast)
For slice As Integer = 0 To slices - 1
Console.WriteLine("Putting a slice of bread in the toaster")
Next
Console.WriteLine("Start toasting...")
Await Task.Delay(3000)
Console.WriteLine("Remove toast from toaster")
Return New Toast()
End Function
Private Async Function FryHashBrownsAsync(patties As Integer) As Task(Of HashBrown)
Console.WriteLine($"putting {patties} hash brown patties in the pan")
Console.WriteLine("cooking first side of hash browns...")
Await Task.Delay(3000)
For patty As Integer = 0 To patties - 1
Console.WriteLine("flipping a hash brown patty")
Next
Console.WriteLine("cooking the second side of hash browns...")
Await Task.Delay(3000)
Console.WriteLine("Put hash browns on plate")
Return New HashBrown()
End Function
Private Async Function FryEggsAsync(howMany As Integer) As Task(Of Egg)
Console.WriteLine("Warming the egg pan...")
Await Task.Delay(3000)
Console.WriteLine($"cracking {howMany} eggs")
Console.WriteLine("cooking the eggs ...")
Await Task.Delay(3000)
Console.WriteLine("Put eggs on plate")
Return New Egg()
End Function
Private Function PourCoffee() As Coffee
Console.WriteLine("Pouring coffee")
Return New Coffee()
End Function
Private Function PourOJ() As Juice
Console.WriteLine("Pouring orange juice")
Return New Juice()
End Function
Private Sub ApplyJam(toast As Toast)
Console.WriteLine("Putting jam on the toast")
End Sub
Private Sub ApplyButter(toast As Toast)
Console.WriteLine("Putting butter on the toast")
End Sub
End Module
Kod parçacığının sonuna yakın bir Await finishedTask ifadesine dikkat edin. Tamamlanan görevi içeren bir Task.WhenAny sarmalayıcı görevi döndürdüğünden Task(Of Task) bu satır önemlidir. bunu yaptığınızda Await Task.WhenAnysarmalayıcı görevinin tamamlanmasını beklersiniz ve sonuç ilk tamamlanan gerçek görevdir. Ancak, bu görevin sonucunu almak veya özel durumların düzgün bir şekilde oluştuğundan emin olmak için tamamlanmış görevin kendisini (içinde Awaitdepolanmış) gerekirfinishedTask. Görevin tamamlandığını biliyor olsanız bile, yeniden beklemek sonucuna erişmenize veya hataya neden olabilecek özel durumları işlemenize olanak tanır.
Son kodu gözden geçirme
Kodun son sürümü şöyle görünür:
Module ConcurrentBreakfastProgram
Async Function Main() As Task
Dim cup As Coffee = PourCoffee()
Console.WriteLine("Coffee is ready")
Dim eggsTask As Task(Of Egg) = FryEggsAsync(2)
Dim hashBrownTask As Task(Of HashBrown) = FryHashBrownsAsync(3)
Dim toastTask As Task(Of Toast) = MakeToastWithButterAndJamAsync(2)
Dim breakfastTasks As New List(Of Task) From {eggsTask, hashBrownTask, toastTask}
While breakfastTasks.Count > 0
Dim finishedTask As Task = Await Task.WhenAny(breakfastTasks)
If finishedTask Is eggsTask Then
Console.WriteLine("eggs are ready")
ElseIf finishedTask Is hashBrownTask Then
Console.WriteLine("hash browns are ready")
ElseIf finishedTask Is toastTask Then
Console.WriteLine("toast is ready")
End If
Await finishedTask
breakfastTasks.Remove(finishedTask)
End While
Dim oj As Juice = PourOJ()
Console.WriteLine("oj is ready")
Console.WriteLine("Breakfast is ready!")
End Function
Async Function MakeToastWithButterAndJamAsync(number As Integer) As Task(Of Toast)
Dim toast As Toast = Await ToastBreadAsync(number)
ApplyButter(toast)
ApplyJam(toast)
Return toast
End Function
Private Async Function ToastBreadAsync(slices As Integer) As Task(Of Toast)
For slice As Integer = 0 To slices - 1
Console.WriteLine("Putting a slice of bread in the toaster")
Next
Console.WriteLine("Start toasting...")
Await Task.Delay(3000)
Console.WriteLine("Remove toast from toaster")
Return New Toast()
End Function
Private Async Function FryHashBrownsAsync(patties As Integer) As Task(Of HashBrown)
Console.WriteLine($"putting {patties} hash brown patties in the pan")
Console.WriteLine("cooking first side of hash browns...")
Await Task.Delay(3000)
For patty As Integer = 0 To patties - 1
Console.WriteLine("flipping a hash brown patty")
Next
Console.WriteLine("cooking the second side of hash browns...")
Await Task.Delay(3000)
Console.WriteLine("Put hash browns on plate")
Return New HashBrown()
End Function
Private Async Function FryEggsAsync(howMany As Integer) As Task(Of Egg)
Console.WriteLine("Warming the egg pan...")
Await Task.Delay(3000)
Console.WriteLine($"cracking {howMany} eggs")
Console.WriteLine("cooking the eggs ...")
Await Task.Delay(3000)
Console.WriteLine("Put eggs on plate")
Return New Egg()
End Function
Private Function PourCoffee() As Coffee
Console.WriteLine("Pouring coffee")
Return New Coffee()
End Function
Private Function PourOJ() As Juice
Console.WriteLine("Pouring orange juice")
Return New Juice()
End Function
Private Sub ApplyJam(toast As Toast)
Console.WriteLine("Putting jam on the toast")
End Sub
Private Sub ApplyButter(toast As Toast)
Console.WriteLine("Putting butter on the toast")
End Sub
End Module
Kod, zaman uyumsuz kahvaltı görevlerini yaklaşık 15 dakika içinde tamamlar. Bazı görevler aynı anda çalıştırıldığından toplam süre azalır. Kod aynı anda birden çok görevi izler ve yalnızca gerektiğinde eylemde bulunur.
Son kod zaman uyumsuzdur. Bir kişinin kahvaltıyı nasıl pişirebileceğini daha doğru bir şekilde yansıtır. Son kodu makaledeki ilk kod örneğiyle karşılaştırın. Temel eylemler, kodu okuyarak hala açıktır. Son kodu, makalenin başında gösterildiği gibi kahvaltı yapma yönergelerini okuduğunuz gibi okuyabilirsiniz.
Async ve Await anahtar sözcüklerinin dil özellikleri, her kişinin yazılı yönergeleri izlemek için yaptığı çeviriyi sağlar: Görevleri olabildiğince başlatın ve görevlerin tamamlanmasını beklerken engellemeyin.
Async/await vs ContinueWith (Programlama dilinde asenkron işlem metotları olan "async/await" ve "ContinueWith" karşılaştırması)
Async ve Await anahtar sözcükleri, doğrudan ContinueWith kullanımına kıyasla dil bilgisel bir basitleştirme sağlar. Zaman uyumsuz işlemleri işlemek için benzer semantiklere sahip olsa da, derleyici Async ifadelerini doğrudan / yöntem çağrılarına çevirmeyebilir.AwaitContinueWithAwaitContinueWith Bunun yerine, derleyici aynı mantıksal davranışı sağlayan iyileştirilmiş durum makine kodu oluşturur. Bu dönüşüm, özellikle birden çok eşzamansız işlemi ardışık hale getirme sırasında önemli okunabilirlik ve sürdürülebilirlik avantajları sağlar.
Birden çok sıralı zaman uyumsuz işlem gerçekleştirmeniz gereken bir senaryo düşünün. İşte aynı mantık ContinueWith ile uygulandığında, Async/Await ile karşılaştırıldığında şu şekilde görünür:
ContinueWith Kullanımı
ContinueWith ile, asenkron işlemler dizisindeki her adım için iç içe geçmiş devamlar gerekir.
' Using ContinueWith - demonstrates the complexity when chaining operations
Function MakeBreakfastWithContinueWith() As Task
Return StartCookingEggsAsync() _
.ContinueWith(Function(eggsTask)
Dim eggs = eggsTask.Result
Console.WriteLine("Eggs ready, starting bacon...")
Return StartCookingBaconAsync()
End Function) _
.Unwrap() _
.ContinueWith(Function(baconTask)
Dim bacon = baconTask.Result
Console.WriteLine("Bacon ready, starting toast...")
Return StartToastingBreadAsync()
End Function) _
.Unwrap() _
.ContinueWith(Function(toastTask)
Dim toast = toastTask.Result
Console.WriteLine("Toast ready, applying butter...")
Return ApplyButterAsync(toast)
End Function) _
.Unwrap() _
.ContinueWith(Function(butteredToastTask)
Dim butteredToast = butteredToastTask.Result
Console.WriteLine("Butter applied, applying jam...")
Return ApplyJamAsync(butteredToast)
End Function) _
.Unwrap() _
.ContinueWith(Sub(finalToastTask)
Dim finalToast = finalToastTask.Result
Console.WriteLine("Breakfast completed with ContinueWith!")
End Sub)
End Function
Async/Await Kullanma
Operasyonların Async/Await kullanıldığı aynı dizisi çok daha doğal bir şekilde okunur.
' Using Async/Await - much cleaner and easier to read
Async Function MakeBreakfastWithAsyncAwait() As Task
Dim eggs = Await StartCookingEggsAsync()
Console.WriteLine("Eggs ready, starting bacon...")
Dim bacon = Await StartCookingBaconAsync()
Console.WriteLine("Bacon ready, starting toast...")
Dim toast = Await StartToastingBreadAsync()
Console.WriteLine("Toast ready, applying butter...")
Dim butteredToast = Await ApplyButterAsync(toast)
Console.WriteLine("Butter applied, applying jam...")
Dim finalToast = Await ApplyJamAsync(butteredToast)
Console.WriteLine("Breakfast completed with Async/Await!")
End Function
Neden Async/Await tercih edilir?
Yaklaşım Async/Await çeşitli avantajlar sunar:
- Okunabilirlik: Kod zaman uyumlu kod gibi okunur ve bu da işlem akışının anlaşılmasını kolaylaştırır.
- Bakım: Sırayla adımları eklemek veya kaldırmak için en az kod değişikliği gerekir.
-
Hata işleme: Bloklarla
Try/Catchözel durum işleme doğal olarak çalışır, ancakContinueWithhatalı görevlerin dikkatli bir şekilde işlenmesini gerektirir. -
Hata ayıklama: çağrı yığını ve hata ayıklayıcı deneyimi ile
Async/Awaitçok daha iyidir. -
Performans: için
Async/Awaitderleyici iyileştirmeleri, el ile gerçekleştirilenContinueWithzincirlerden daha karmaşıktır.
Zincirleme işlemlerin sayısı arttıkça avantaj daha da belirgin hale gelir. ile ContinueWith tek bir devamlılık yönetilebilir olsa da, 3-4 veya daha fazla eşzamansız işlem dizilerinin okunması ve bakımı hızla zorlaşır. İşlevsel programlamada "monadik do-notasyonu" olarak bilinen bu desen, birden çok zaman uyumsuz işlemi sıralı, okunabilir bir şekilde bir araya getirmenize olanak tanır.