Geçersizliği iade etmek ile bir Görevi iade etmek arasındaki fark nedir?


128

Çeşitli C # Async CTP örneklerine baktığımda, dönen bazı eşzamansız işlevler voidve genel olmayan döndüren diğerleri görüyorum Task. Zaman Task<MyType>uyumsuz işlem tamamlandığında arayan kişiye veri döndürmek için a döndürmenin neden yararlı olduğunu anlayabiliyorum , ancak gördüğüm işlevler Taskhiçbir zaman veri döndürmüyor. Neden geri dönmüyorsun void?

Yanıtlar:


214

SLaks ve Killercam'ın cevapları iyi; Biraz daha içerik ekleyeceğimi düşündüm.

İlk sorunuz esas olarak hangi yöntemlerin işaretlenebileceğiyle ilgilidir async.

Olarak işaretlenmiş bir yöntem asyncdönebilir void, Taskya da Task<T>. Aralarındaki farklar nelerdir?

Bir Task<T>dönen zaman uyumsuz yöntem beklenen edilebilir ve görev tamamlandığında bir T. yukarı buyruğu olacak

Bir Taskdönen zaman uyumsuz yöntem beklenen edilebilir ve görev tamamlamalar, görevin devamı çalıştırmak üzere programlandığı.

Bir voiddönen uyumsuz yöntemi beklenilemeyecek; bu bir "ateş et ve unut" yöntemidir. Zaman uyumsuz olarak çalışır ve ne zaman bittiğini söylemenizin hiçbir yolu yoktur. Bu biraz tuhaf olmaktan çok daha fazlası; SLaks'ın dediği gibi, normalde bunu yalnızca zaman uyumsuz bir olay işleyicisi yaparken yaparsınız. Olay tetiklenir, işleyici çalıştırır; hiç kimse olay işleyicisi tarafından döndürülen görevi "beklemeyecektir" çünkü olay işleyicileri görevleri döndürmez ve dönseler bile, Görev'i bir şey için hangi kod kullanır? Genellikle kontrolü ilk etapta işleyiciye aktaran kullanıcı kodu değildir.

Bir yorumdaki ikinci sorunuz, esasen nelerin awaitöğrenilebileceğiyle ilgilidir:

Ne tür yöntemler geliştirilebilir await? Boşluk döndürme yöntemi kullanılabilir awaitmi?

Hayır, boşluk döndürme yöntemi beklenemez. Derleyici , bir örnek yöntemi veya bir uzantı yöntemi olabilen await M()bir çağrıya çevirir . Beklenen değer, sizi bekleyen bir değer olmalıdır; Açıkça bir boşluk döndürme yöntemi, sizi bekleyen bir değer elde edebileceğiniz bir değer üretmez.M().GetAwaiter()GetAwaiter

Task-dönüş yöntemleri, beklenebilir değerler üretebilir. Üçüncü şahısların, beklenebilecek Tasknesneye benzer kendi uygulamalarını yaratmak isteyeceklerini ve siz de onları bekleyebileceğinizi tahmin ediyoruz. Ancak, beyan izin verilmeyecektir asyncdönüş şey ama bu yöntemler void, Taskya Task<T>.

(GÜNCELLEME: Son cümlem, C # 'ın gelecekteki bir sürümü tarafından tahrif edilmiş olabilir; zaman uyumsuz yöntemler için görev türleri dışında dönüş türlerine izin veren bir teklif var.)

(GÜNCELLEME: Yukarıda bahsedilen özellik C # 7'ye girdi.)


7
+1 Bence eksik olan tek şey, void döndüren zaman uyumsuz yöntemlerde istisnaların nasıl ele alındığı arasındaki fark.
João Angelo

10
@JamesCadd: Eşzamansız bazı çalışmaların bir istisna attığını varsayalım. Kim yakalar? Eşzamansız görevi başlatan kod artık yığın üzerinde değil - aynı iş parçacığında bile olmayabilir - ve istisnalar, tüm yakalama / son olarak blokların yığında olduğunu varsayar . Ee ne yapıyorsun? İstisna bilgilerini, daha sonra inceleyebilmeniz için Görevde saklıyoruz. Ancak yöntem geçersizse, kullanıcı kodunun kullanabileceği bir Görev yoktur. Bu durumla tam olarak nasıl başa çıktığımız bazı tartışmalara konu oldu ve şu anda neye karar verdiğimizi hatırlamıyorum.
Eric Lippert

8
BUILD'de Stephen Toub'a bu soruyu sordum. . 4.5'te, varsayılan davranışı değiştirdiler, böylece gözlenmeyen istisnalar TaskScheduler :: UnobservedTaskException olayıyla raporlanmaya devam edecek, ancak artık işlemi çökertmeyecek. Eğer varsa istediğiniz eski 4,0 davranışı sizinle tekrar etkinleştirebilirsiniz <zamanı> <ThrowUnobservedTaskExceptions etkin = / "true"> </ çalışma zamanı>. Büyük olasılıkla değişiklik, geçersiz eşzamansız yöntemler için ateşle ve unut yöntemlerini desteklemek amacıyla yapılmıştır.
Drew Marsh

4
async voidyöntemler SynchronizationContext, yürütmeye başladıkları sırada etkin olan istisnalarını yükseltir . Bu, (eşzamanlı) olay işleyicilerinin davranışına benzer. @DrewMarsh: UnobservedTaskExceptionve çalışma zamanı ayarı , yöntemlere değil, yalnızca "çalıştır ve unut" zaman uyumsuz Görevasync void yöntemlerine uygulanır.
Stephen Cleary


23

Arayanın görevi beklemek veya bir devam eklemek istemesi durumunda.

Aslında, dönüş tek nedeni voideğer olduğunu olamaz dönmek Taskbir olay işleyicisi yazıyoruz çünkü.


Bir void türü döndüren yöntemleri de beklemenin mümkün olduğunu düşündüm - biraz ayrıntı verebilir misiniz?
James Cadd

1
Hayır, yapamazsınız. Yöntem geri dönerse void, ürettiği göreve ulaşmanın hiçbir yolu yoktur. (Aslında, bir tane oluşturup oluşturmadığından bile emin değilim Task)
SLaks

18

Geri dönen yöntemler Taskve bir araya Task<T>getirilebilir - yani awaitbunları bir asyncyöntemin içinde yapabilirsiniz .

asyncGeri dönen yöntemler oluşturulamaz void, ancak iki önemli özelliği daha vardır:

  1. Olay işleyicileri olarak kullanılabilirler.
  2. "En üst düzey" eşzamansız işlemi temsil ederler.

İkinci nokta, olağanüstü eşzamansız işlemlerin sayısını koruyan bir bağlamla uğraşırken önemlidir .

ASP.NET bağlamı böyle bir bağlamdır; zaman uyumsuz Taskyöntemleri bir zaman uyumsuz voidyöntemden beklemeden kullanırsanız , ASP.NET isteği çok erken tamamlanacaktır.

Başka bir bağlam, AsyncContextbirim testi için yazdığım ( burada mevcuttur ) - AsyncContext.Runyöntem, olağanüstü işlem sayısını izler ve sıfır olduğunda geri döner.


12

Tür Task<T>, Görev Paralel Kitaplığı'nın (TPL) iş gücü türüdür T, "gelecekte türden bir sonuç üretecek bazı işler / işler" kavramını temsil eder . "Gelecekte tamamlanacak ancak sonuç vermeyen iş" kavramı, genel olmayan Görev türü ile temsil edilir.

Tipin sonucunun tam olarak nasıl Türetileceği ve belirli bir görevin uygulama detayı; iş yerel makinedeki başka bir işleme, başka bir iş parçacığına vb. eklenebilir. TPL görevleri tipik olarak mevcut süreçteki bir iş parçacığı havuzundan çalışan iş parçacıklarına aktarılır, ancak bu uygulama ayrıntısı tür için temel değildir Task<T>; bunun yerine a Task<T>, a üreten herhangi bir yüksek gecikmeli işlemi temsil edebilir T.

Yukarıdaki yorumunuza göre:

awaitSentezleme araçları "görevi üretilmesi durumunda. Geri bu görevin ile ortaya çıkan araması olarak mevcut yöntemin kalan Kaydol. İleride olacak bir sonuç bu işi temsil eden bir nesne elde etmek için bu ekspresyonunu değerlendirmek ve geri arama kayıt oldu, kontrolü hemen arayana iade et ". Bu, "ne yaptığınızı hatırlayın, bu yöntemi tamamen bitene kadar çalıştırın ve sonra kaldığınız yerden devam edin, artık yöntemin sonucunu bilerek" anlamına gelen normal bir yöntem çağrısına zıttır / zıttır.


Düzenleme: Eric Lippert'in Ekim 2011'de MSDN Magazine'deki makalesine atıfta bulunmalıyım, çünkü bu, bu konuyu anlamamda bana çok yardımcı oldu.

Daha fazla bilgi ve beyaz sayfa için buraya bakın .

Umarım bu biraz yardımcı olur.

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.