2015-01-29 19 views
8

this question'u takip ediyorum ve Peter Duniho tarafından popüler (arkasındaki kabul edilmemiş olsa da) yanıtın nedenlerini anlıyorum. Özellikle, değil müteakip uzun süren operasyon UI iş parçacığı engeller bekleyen farkındayım:Yuvalanmış bekletme işlemleri beklenir mi?

ikinci örnek asenkron çalışma sırasında ortaya koymamıştır. Bunun yerine, content.Result özelliği değerini alarak, asıl iş parçacığı, asenkron işlem tamamlanana kadar beklemeye zorlar.

Hatta, kendi yararı için bu, teyit şöyle ettik:

private async void button1_Click(object sender, EventArgs e) 
{ 
    var value1 = await Task.Run(async() => 
     { 
      await Task.Delay(5000); 
      return "Hello"; 
     }); 

    //NOTE: this one is not awaited... 
    var value2 = Task.Run(async() => 
     { 
      await Task.Delay(5000); 
      return value1.Substring(0, 3); 
     }); 

    System.Diagnostics.Debug.Print(value2.Result); //thus, UI freezes here after 5000 ms. 
} 

Ama şimdi merak ediyorum: Bir dıştaki awaitable içinde yuvalanmış await bütün "awaitable" operasyonlar gerekiyor operasyon?

private async void button1_Click(object sender, EventArgs e) 
{ 
    var value0 = await Task.Run(() => 
     { 
      var value1 = new Func<Task<string>>(async() => 
      { 
       await Task.Delay(5000); 
       return "hello"; 
      }).Invoke(); 

      var value2 = new Func<string, Task<string>>(async (string x) => 
       { 
        await Task.Delay(5000); 
        return x.Substring(0, 3); 
       }).Invoke(value1.Result); 

      return value2; 
     }); 

    System.Diagnostics.Debug.Print(value0); 
} 

Veya bunu yapabilirsiniz:: Örneğin, ben bunu yapabilirsiniz

private async void button1_Click(object sender, EventArgs e) 
{ 
    //This time the lambda is async... 
    var value0 = await Task.Run(async() => 
     { 
      //we're awaiting here now... 
      var value1 = await new Func<Task<string>>(async() => 
      { 
       await Task.Delay(5000); 
       return "hello"; 
      }).Invoke(); 

      //and we're awaiting here now, too... 
      var value2 = await new Func<string, Task<string>>(async (string x) => 
       { 
        await Task.Delay(5000); 
        return x.Substring(0, 3); 
       }).Invoke(value1); 

      return value2; 
     }); 

    System.Diagnostics.Debug.Print(value0); 
} 

Ve ikisi de UI dondurun. Hangisi tercih edilir?

+0

Ben hepsi aynı şeyi yazdırmak olanlar ise bir çeşit Görev nesnesi yazdırmakta olduğunu düşünürsek çok şaşıracaksınız Sonuçtan ziyade. –

+0

@BenVoigt - Hayır, bc'de her iki durumda da görev beklendiğinden beri açılmıyor. –

+0

Ama bunlardan biri ikinci bir Görev. –

cevap

13

sonuncusu (oldukça dağınık halde)

TAP (görev temelli Asenkron Desen) bir görev (ve diğer awaitables) 'de bir zaman uyumsuz işlem temsil tercih edilir. Görev tamamlanana kadar Bloklar çağıran iş parçacığı -

  • eşzamanlı bekleyin (DoAsync().Result, DoAsync().Wait()): Bu görevleri taşıma temelde 3 seçenek var. Uygulamanızı daha savurgan, daha az ölçeklenebilir, daha az duyarlı ve kilitlenmelere karşı hassas hale getirir.
  • Zaman uyumsuz olarak bekleyin (await DoAsync()) - Arama iş parçacığını engellemiyor. Temelde, bekletilen görev tamamlandıktan sonra yürütülecek bir devam etme olarak await'dan sonra işi kaydeder.
  • Hiç beklemeyin (DoAsync()) - Arama iş parçacığı engellenmez, aynı zamanda işlemin tamamlanmasını beklemez. Benim UI konuyu

    Yani, Pek engeller müteakip uzun süreli işlem beklemiyor farkındayım, DoAsync iken atılan herhangi bir istisna habersiz Özellikle

işlenir vardır. Eğer hiç beklemezseniz, hiçbir şey engellenmez ancak işlemin ne zaman başarılı bir şekilde tamamlandığını bilemezsiniz. Ancak, eşzamanlı olarak beklerseniz, arama iş parçacığını engellersiniz ve kullanıcı arabirimi iş parçacığını engelliyorsanız kilitlenmeler olabilir.

Sonuç: await Avantajlarınıza mümkün olduğu sürece sahip olmalısınız (örneğin, Main'da değil). Bu "iç içe async-await işlemleri" içerir.

Özel örneğiniz hakkında: Task.Run, CPU bağlı çalışmayı bir taklit etmeye çalıştığınız gibi görünmeyen bir ThreadPool iş parçacığına yüklemek için kullanılır.Biz gerçekten uyumsuz işlemi temsil etmek Task.Delay kullanırsanız (genellikle G/Ç bağımlı) biz Task.Run olmadan "async-await yuvalanmış" gelmiş olabilir:

private async void button1_Click(object sender, EventArgs e) 
{ 
    var response = await SendAsync(); 
    Debug.WriteLine(response); 
} 

async Task<Response> SendAsync() 
{ 
    await SendRequestAsync(new Request()); 
    var response = await RecieveResponseAsync(); 
    return response; 
} 

async Task SendRequestAsync(Request request) 
{ 
    await Task.Delay(1000); // actual I/O operation 
} 

async Task<Response> RecieveResponseAsync() 
{ 
    await Task.Delay(1000); // actual I/O operation 
    return null; 
} 

yerine yöntemlerden anonim delegelere kullanabilirsiniz, ama ihtiyacınız olduğunda rahatsız türleri tanımlamak ve bunları kendiniz çağırmak. Eğer bir ThreadPool konuya bu işlem yükünden gerekmiyorsa

sadece Task.Run ekleyin:

private async void button1_Click(object sender, EventArgs e) 
{ 
    var response = await Task.Run(() => SendAsync()); 
    Debug.WriteLine(response); 
} 
+0

Aslında cevabınızı tekrar okuyorum ve açıklığa kavuşturabilir misiniz diye merak ediyorum: * iç içe geçmiş beklemeler kullanmam gerektiğini mi söylüyorsun (benim üçüncü kod bloğumun yaptığı şey) "Ya da bunu yapabilirim")? –

+0

@roryap Bence operasyonlar “uyumsuz” ise, nerede olduklarından bağımsız olarak engellemek yerine onları bekliyorlar. Yaptığınız işleve bağlı olarak bir kez veya iki kez 'Task.Run' sıfır kullanılıp kullanılmayacağı hakkında * aslında * yapmaya çalışıyorum ('Task.Delay' burada sadece bir yedek var). – i3arnon

+1

@roryap Eğer 'Task.Run' işlevini kullanmak istediğiniz uzun bir CPU yoğun işleminiz varsa, aksi takdirde basitçe 'async' işlemlerinizi çağırırsınız ve onları beklersiniz. – i3arnon

İlgili konular