2013-10-17 25 views
59

Koleksiyonda bazı çalışmalar yapan eşzamansız bir konsol uygulaması oluşturmaya çalışıyorum. Döngüyü/uyumlamayı kullanan başka bir sürüm için döngü için paralel kullanan bir sürümüm var. Async/await versiyonunun paralel versiyona benzer şekilde çalışmasını bekledim ama senkronize olarak çalışıyor. Neyi yanlış yapıyorum?Bir döngüde bekletme süresi nasıl kullanılır

class Program 
{ 
    static void Main(string[] args) 
    { 
     var worker = new Worker(); 
     worker.ParallelInit(); 
     var t = worker.Init(); 
     t.Wait(); 
     Console.ReadKey(); 
    } 
} 

public class Worker 
{ 
    public async Task<bool> Init() 
    { 
     var series = Enumerable.Range(1, 5).ToList(); 
     foreach (var i in series) 
     { 
      Console.WriteLine("Starting Process {0}", i); 
      var result = await DoWorkAsync(i); 
      if (result) 
      { 
       Console.WriteLine("Ending Process {0}", i); 
      } 
     } 

     return true; 
    } 

    public async Task<bool> DoWorkAsync(int i) 
    { 
     Console.WriteLine("working..{0}", i); 
     await Task.Delay(1000); 
     return true; 
    } 

    public bool ParallelInit() 
    { 
     var series = Enumerable.Range(1, 5).ToList(); 
     Parallel.ForEach(series, i => 
     { 
      Console.WriteLine("Starting Process {0}", i); 
      DoWorkAsync(i); 
      Console.WriteLine("Ending Process {0}", i); 
     }); 
     return true; 
    } 
} 

cevap

78

Eğer await anahtar kelimeyi kullandığınız yolu beklemek istiyorum C# söyler: Sadece hepsini bekler bir görev dönmek için Task s koleksiyonu almak ve Task.WhenAll() çağırmanız gerekir Döngüden geçtiğiniz her zaman, paralel olmayan. İstediğiniz şeyi yapmak için, Task s listesini ve daha sonra await hepsini Task.WhenAll ile kaydederek, yönteminizi bu şekilde yeniden yazabilirsiniz.

public async Task<bool> Init() 
{ 
    var series = Enumerable.Range(1, 5).ToList(); 
    var tasks = new List<Task<Tuple<int, bool>>>(); 
    foreach (var i in series) 
    { 
     Console.WriteLine("Starting Process {0}", i); 
     tasks.Add(DoWorkAsync(i)); 
    } 
    foreach (var task in await Task.WhenAll(tasks)) 
    { 
     if (task.Item2) 
     { 
      Console.WriteLine("Ending Process {0}", task.Item1); 
     } 
    } 
    return true; 
} 

public async Task<Tuple<int, bool>> DoWorkAsync(int i) 
{ 
    Console.WriteLine("working..{0}", i); 
    await Task.Delay(1000); 
    return Tuple.Create(i, true); 
} 
+2

Diğerleri hakkında bilmiyorum, ama foreach for parallel for parallel döngüler için daha düz görünüyor. – Brettski

+2

Görev bittiğinde, "Bitiş Süreci" bildiriminin ** görünmediğini unutmayın. Tüm bu bildirimler, görevlerin son * bitmesinden hemen sonra sıralı olarak bırakılır. Süreç 1'i sonlandırdığımızda, süreç 1 uzun bir süredir bitmiş olabilir. Orada kelime seçiminden başka, +1. –

+0

@Brettski Yanlış olabilir, ancak paralel döngü, herhangi bir asenkron sonucu yakalar. Bir Görev 'u döndüğünüzde, onu iptal etmek veya istisnaları görmek gibi içerideki işleri yönetebileceğiniz bir Görev nesnesini hemen geri alırsınız. Artık Async/Await ile Görev nesnesini daha samimi bir şekilde çalıştırabilirsiniz - Task.Result yapmak zorunda değilsiniz. –

27

Kodunuz sonraki yinelemesi başlamadan önce bitirmek için (await kullanarak) her operasyon için bekler.
Bu nedenle, herhangi bir paralellik elde edemezsiniz.

Varolan bir eşzamansız işlemi paralel olarak çalıştırmak istiyorsanız, await;

return Task.WhenAll(list.Select(DoWorkAsync)); 
+0

Bu nedenle, herhangi bir döngüde herhangi bir zaman uyumsuz yöntem kullanamazsınız? – Satish

+3

@Satish: Yapabilirsiniz. Ancak, 'bekle' istediğini tam tersi yapar - 'Görev' için bitirmek için _waits_. – SLaks

+0

Cevabınızı kabul etmek istedim ama Tims S'nin daha iyi bir cevabı var. – Satish

7
public async Task<bool> Init() 
{ 
    var series = Enumerable.Range(1, 5); 
    Task.WhenAll(series.Select(i => DoWorkAsync(i))); 
    return true; 
} 
İlgili konular