Önemli bir fark, istisna yayılımıdır. Bir içinde atılan bir istisna, async Taskyöntem, geri depolanır Tasknesne ve görev ile gözlenen alana kadar atıl kalır await task, task.Wait(), task.Resultya da task.GetAwaiter().GetResult(). Yöntemin eşzamanlı kısmından atılsa bile bu şekilde yayılır async.
Şu kodu göz önünde bulundurun, burada OneTestAsyncve AnotherTestAsyncoldukça farklı davranır:
static async Task OneTestAsync(int n)
{
await Task.Delay(n);
}
static Task AnotherTestAsync(int n)
{
return Task.Delay(n);
}
static void DoTestAsync(Func<int, Task> whatTest, int n)
{
Task task = null;
try
{
task = whatTest(n);
Console.Write("Press enter to continue");
Console.ReadLine();
task.Wait();
}
catch (Exception ex)
{
Console.Write("Error: " + ex.Message);
}
}
Ben ararsam DoTestAsync(OneTestAsync, -2), aşağıdaki çıktıyı üretir:
Devam etmek için enter tuşuna basın
Hata: Bir veya daha fazla hata oluştu. Await Task.Delay
Hata: 2.
Not, Entergörmek için basmam gerekti.
Şimdi ararsam DoTestAsync(AnotherTestAsync, -2) , içindeki kod iş akışı DoTestAsyncoldukça farklıdır ve çıktı da öyle. Bu sefer basmam istenmedi Enter:
Hata: Değerin -1 (sonsuz bir zaman aşımı anlamına gelir), 0 veya pozitif bir tam sayı olması gerekir.
Parametre adı: milisaniyeDelayError: 1.
Her iki durumda da Task.Delay(-2), parametrelerini doğrularken başlangıca atar. Bu uydurma bir senaryo olabilir, ancak teorideTask.Delay(1000) , örneğin temel sistem zamanlayıcı API'si başarısız olduğunda da fırlatabilir.
Bir yan not olarak, hata yayılma mantığı async voidyöntemler için henüz farklıdır ( yöntemlerin aksine async Task). Bir async voidyöntemin içinde ortaya çıkan bir istisna SynchronizationContext.Post, mevcut iş parçacığının bir tane varsa ( SynchronizationContext.Current != null).ThreadPool.QueueUserWorkItem ). Arayan kişinin bu istisnayı aynı yığın çerçevesinde işleme şansı yoktur.
Burada ve burada TPL istisna işleme davranışı hakkında daha fazla ayrıntı yayınladım .
S : asyncEşzamansız olmayan yöntemler için yöntemlerin istisna yayılma davranışını taklit etmek Task, böylece ikincisi aynı yığın çerçevesine atılmaması mümkün müdür ?
C : Gerçekten gerekliyse, evet, bunun için bir numara var:
async Task<int> MethodAsync(int arg)
{
if (arg < 0)
throw new ArgumentException("arg");
return 42 + arg;
}
Task<int> MethodAsync(int arg)
{
var task = new Task<int>(() =>
{
if (arg < 0)
throw new ArgumentException("arg");
return 42 + arg;
});
task.RunSynchronously(TaskScheduler.Default);
return task;
}
Bununla birlikte, belirli koşullar altında (yığında çok derin olduğunda olduğu gibi) RunSynchronouslyyine de eşzamansız olarak çalıştırılabileceğini unutmayın.
Bir başka önemli fark olduğunu
/ versiyon ölü kilit varsayılan olmayan bir senkronizasyon bağlamına daha yatkın olduğunu . Örneğin, aşağıdakiler bir WinForms veya WPF uygulamasında kilitlenecektir:
asyncawait
static async Task TestAsync()
{
await Task.Delay(1000);
}
void Form_Load(object sender, EventArgs e)
{
TestAsync().Wait();
}
Eşzamansız olmayan bir sürüme değiştirin ve kilitlenmeyecektir:
Task TestAsync()
{
return Task.Delay(1000);
}
Kilitlenmenin doğası Stephen Cleary tarafından blogunda iyi açıklanmıştır .
await/asynchepsi :) en