2012-12-28 26 views
21

Ben async test ediyorum ve ben anlayamıyorum bu durumu tespit yılında Task.Delay `nın çalışması: sonucu her zaman 0'dır neden istiyoruz Neden bu duruma

var watch = Stopwatch.StartNew(); 

var t1 = Task.Factory.StartNew(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}); 

var t2 = Task.Factory.StartNew(() => 
{ 
    Task.Delay(1000); 

    return 1; 
}); 

await Task.WhenAll(t1, t2); 

var result = watch.ElapsedMilliseconds; 

anlamak ! Neden 1000, 2000 veya iki görev 3000 toplamı değil? Neden Task.WhenAll görevlerin tamamlanmasını beklemiyor?

+0

@GrantThomas Bir değişkene atamaya çalışıyorum ama bana bir derleyici hatası veriyor, bunu nasıl yapabilirim? – MuriloKunze

+0

@MuriloKunze Bu soruyu cevaplamak için kod satırını ve hatayı görmemiz gerekiyor. – Servy

+0

Bunu denedim: var taskresult = Task.WhenAll (t1, t2); Ama bana bir 'Yazılmış bir yerel değişkene void atanamaz' verir .. – MuriloKunze

cevap

41

Tamam, öyleyse, ikincisi kolay olanı, o yüzden halledelim.

İkinci görev için t2, Task.Delay(1000) sonucuyla hiçbir şey yapmazsınız. await yapmıyorsunuz, Wait yapmıyorsunuz, vb. Yöntemin async olmadığı göz önünde bulundurulduğunda, bunun engelleme beklemesi olması gerektiği kanaatindeyim. Bunu yapmak için, Delay aramasının sonuna Wait() numaralı çağrıyı eklemek istediğinizde, engelleme beklemesi yapmak veya Thread.Sleep()'u kullanmak istiyorsunuz. İlk görev için


, size var kullandığınız gerçeğiyle ısırılan ediyoruz.

Task<Task<int>> t1 = Task.Factory.StartNew(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}); 

Sen, sadece bir Taskint ait int görevi bir görevi dönüyoruz: Eğer var kullanmayın zaman neler olduğunu daha açık. İç görev tamamlandığında, dış görev "tamamlandı". WhenAll'u kullandığınızda, dış görevin sona ermesiyle ilgilenmezsiniz, görevinin ne zaman biteceğini önemsersiniz. Bunu halletmenin bir kaç yolu var. Biri Unwrap'u kullanmaktır.

Task<int> t1 = Task.Factory.StartNew(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}).Unwrap(); 

Şimdi beklendiği gibi, en az 2000 milisaniye sürer Task<int> ve WhenAll bekleniyor var. Ayrıca aynı şeyi yapmak için başka await çağrısında ekleyebilirsiniz:

başka seçenek sadece Task.Run yerine StartNew kullanmak olacaktır svick in a comment tarafından belirtildiği gibi
Task<int> t1 = await Task.Factory.StartNew(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}); 

.

Task<int> t1 = Task.Run(async() => 
{ 
    await Task.Delay(2000); 

    return 2; 
}); 

varsayılan seçenek olarak Task.Run kullanmak o tercih edilir Bu nedenle size zaman uyumsuz lambdas oluştururken: Task.Run sizin için Func<Task<T>> alıp bir Task<T> dönmek ve bunları otomatik paketini yöntemleri için aşırı özel bir kümesi vardır Task.Run'u kullanamadığınız karmaşık durumlar için farkında olmanız en iyisi olsa da, sizin için bu sorunu "ele alacaktır".


Sonunda muhtemelen bu durumda yapıyor aslında olması gereken budur sen yapmadığı seçeneği, gel. Task.Delay zaten bir Görev döndürdüğü için, ilk etapta StartNew'a koymanıza gerek yoktur. Aksine yuvalanmış görev oluşturma ve Unwrap kullanmaktan daha sadece ilk etapta bunu sarın edemez:

var t3 = Task.Delay(3000); 
await Task.WhenAll(t1, t2, t3); 

aslında sadece yapıyor olması gerekenler zamanın sabit bir tutarda beklemek istersen.

+0

+1 büyük yanıt için –

+0

Harika bir yanıt :) – MuriloKunze

+4

Başka bir seçenek var: 'Task.Run()' kullanın. Bu görevi kendiliğinden açmaz, bu yüzden 'Unwrap()' ile birleştirilmiş olan 'StartNew() 'i kullanmak daha iyi bir seçenek olabilir. – svick