Task.Start / Wait ve Async / Await arasındaki fark nedir?


206

Bir şey eksik olabilir ama yapmak arasındaki fark nedir:

public void MyMethod()
{
  Task t = Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();
  UpdateLabelToSayItsComplete();
}

public async void MyMethod()
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  UpdateLabelToSayItsComplete();
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}

Yanıtlar:


395

Bir şey kaçırıyor olabilirim

Sen.

doing Task.Waitve arasındaki fark await tasknedir?

Öğle yemeğini restoranda garsondan sipariş edersiniz. Siparişinizi verdikten bir dakika sonra, bir arkadaşınız içeri girer ve yanınızda oturur ve bir konuşma başlatır. Şimdi iki seçeneğiniz var. Görev tamamlanana kadar arkadaşınızı görmezden gelebilirsiniz - çorbanız gelene kadar bekleyebilir ve beklerken başka bir şey yapamazsınız. Ya da arkadaşınıza cevap verebilirsiniz ve arkadaşınız konuşmayı bıraktığında, garson size çorbanızı getirecektir.

Task.Waitgörev tamamlanana kadar engeller - görev tamamlanana kadar arkadaşınızı yok sayarsınız. awaitiletileri ileti kuyruğunda işlemeye devam eder ve görev tamamlandığında "bekledikten sonra kaldığınız yerden devam et" yazan bir iletiyi sırayla söyler. Arkadaşınızla konuşuyorsunuz ve konuşmada bir ara olduğunda çorba geliyor.


5
@ronag Hayır, değil. Task10 ms süren bir beklemenin aslında Taskiplik üzerinde 10 saat sürecek ve böylece sizi 10 saat boyunca engelleyecek olsaydı nasıl istersiniz ?
svick

62
@StrugglingCoder: bekliyoruz operatör değil yapmak dışında bir şey onun işlenen değerlendirmek ve daha sonra hemen şimdiki arayana bir görevi iade . İnsanlar bu fikrini, zaman uyumsuzluğunun ancak iş parçacıklarına boşaltma yoluyla başarılabileceği fikrini alıyorlar, ama bu yanlış. Tost makinesi ekmek kızartma makinesindeyken, ekmek kızartma makinesini izlemek için bir aşçı tutmadan kahvaltı yapabilir ve kağıdı okuyabilirsiniz. İnsanlar, ekmek kızartma makinesinin içine gizlenmiş bir iplik - işçi - olması gerektiğini söylüyor, ancak tost makinenize bakarsanız, tostu izleyen küçük bir adam olmadığını temin ederim.
Eric Lippert

11
@StrugglingCoder: Peki, sorduğunuz işi kim yapıyor? Belki başka bir iş parçacığı işi yapıyor ve bu iş parçacığı bir CPU'ya atandı, bu yüzden iş aslında yapılıyor. Belki iş donanım tarafından yapılıyor ve hiç iplik yok. Ama elbette, donanımda bir iplik olması gerektiğini söylüyorsunuz . Hayır. Donanım iş parçacığı düzeyinin altında. Konuya gerek yok! Stephen Cleary'nin Konu Yok makalesini okumaktan faydalanabilirsiniz.
Eric Lippert

6
@StrugglingCoder: Şimdi, soru, diyelim ki asenkron çalışma yapılıyor ve donanım yok ve başka bir iş parçacığı yok. Bu nasıl mümkün olabilir? Peki, beklediğiniz şeyin bir dizi pencere mesajı sıraladığını varsayalım , her biri biraz işe yarıyor mu? Şimdi ne olacak? Denetimi ileti döngüsüne döndürürsünüz, iletileri kuyruktan çıkarmaya başlar, her seferinde biraz iş yapar ve yapılan son iş "görevin devam etmesini yürütür". Fazladan iplik yok!
Eric Lippert

8
@StrugglingCoder: Şimdi, az önce söylediklerimi düşün. Windows'un bu şekilde çalıştığını zaten biliyorsunuz . Bir dizi fare hareketi ve düğme tıklaması gerçekleştirirsiniz. Mesajlar sıraya alınır, sırayla işlenir, her mesaj çok az iş yapılmasına neden olur ve her şey bittiğinde sistem devam eder. Bir iş parçacığı üzerindeki zaman uyumsuzluk, alışkın olduğunuz şeyden başka bir şey değildir: büyük görevleri küçük bitlere ayırmak, sıraya koymak ve tüm küçük bitleri bir sırada yürütmek. Bu infazlardan bazıları başka işlerin sıraya alınmasına neden olur ve hayat devam eder. Bir iş parçacığı!
Eric Lippert

121

Eric'in cevabını burada göstermek için bazı kodlar var:

public void ButtonClick(object sender, EventArgs e)
{
  Task t = new Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();  
  //If you press Button2 now you won't see anything in the console 
  //until this task is complete and then the label will be updated!
  UpdateLabelToSayItsComplete();
}

public async void ButtonClick(object sender, EventArgs e)
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  //If you press Button2 now you will see stuff in the console and 
  //when the long method returns it will update the label!
  UpdateLabelToSayItsComplete();
}

public void Button_2_Click(object sender, EventArgs e)
{
  Console.WriteLine("Button 2 Clicked");
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}

27
Kod için +1 (yüzlerce okumaktan bir kez çalıştırmak daha iyidir). Ancak " //If you press Button2 now you won't see anything in the console until this task is complete and then the label will be updated!" ifadesi yanıltıcıdır. Düğmeye basıldığında t.Wait();düğmesini tıklatın olay işleyicisi içinde ButtonClick()bu pres şey mümkün değildir ve GUI GUI ile herhangi bir tıklama veya etkileşimleri olduğunu, dondurulmuş ve tepkisiz olduğu için "bu görevi tamamlanana kadar" o zaman konsolu ve etiketin güncellemesinde şey görmek ediliyor KAYIP görev bekleyen tamamlanıncaya kadar
Gennady Vanin Геннадий Ванин

2
Sanırım Eric, Görev api'si hakkında temel bir anlayışa sahip olduğunuzu varsayar. Ben bu koda bakıp kendi kendime " t.Waitgörev tamamlanana kadar yup ana iş parçacığına engel olacak " diyorum .
Muffin Man

50

Bu örnek, farkı çok net bir şekilde göstermektedir. Zaman uyumsuz / beklemede, çağrı dizisi engellenmez ve yürütülmeye devam etmez.

static void Main(string[] args)
{
    WriteOutput("Program Begin");
    // DoAsTask();
    DoAsAsync();
    WriteOutput("Program End");
    Console.ReadLine();
}

static void DoAsTask()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
    WriteOutput("2 - Task started");
    t.Wait();
    WriteOutput("3 - Task completed with result: " + t.Result);
}

static async Task DoAsAsync()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
    WriteOutput("2 - Task started");
    var result = await t;
    WriteOutput("3 - Task completed with result: " + result);
}

static int DoSomethingThatTakesTime()
{
    WriteOutput("A - Started something");
    Thread.Sleep(1000);
    WriteOutput("B - Completed something");
    return 123;
}

static void WriteOutput(string message)
{
    Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, message);
}

DoAsTask Çıkışı:

[1] Program Başladı
[1] 1 - Başlatma
[1] 2 - Görev başladı
[3] A - Bir şey başlattı
[3] B - Bir şey tamamladı
[1] 3 - Görev sonuçlandı: 123
[1] Program Sonu

DoAsAsync Çıkışı:

[1] Program Başladı
[1] 1 - Başlatma
[1] 2 - Görev başladı
[3] A - Bir şey başlattı
[1] Program Sonu
[3] B - Bir şey tamamladı
[3] 3 - Görev sonuçlandı: 123

Güncelleme: Çıktıda iş parçacığı kimliğini göstererek geliştirilmiş örnek.


4
Ama eğer yaparsam: yeni Görev (DoAsTask) .Start (); DoAsAsync () yerine; Ben aynı işlevselliği olsun, bu yüzden nerede faydası bekliyor ..
omriman12

1
Teklifinizle, görevin sonucu başka bir yerde, belki başka bir yöntemde veya lambdada değerlendirilmelidir. Eşzamansız bekleme, eşzamansız kodu izlemeyi kolaylaştırır. Sadece bir sözdizimi geliştiricisi.
Mas

@Mas, Program Sonu'nun A'dan sonra neden olduğunu anlamıyorum - Bir şey başlattı. Anahtar kelime sürecini beklemek söz konusu olduğunda benim anlayışımdan hemen ana içeriğe gitmeli ve sonra geri dönmeliyim.

@JimmyJimm Benim görevime göre Task.Factory.StartNew, DoSomethingThatTakesTime'ı çalıştırmak için yeni bir iş parçacığı oluşturacak. Bu nedenle, öncelikle Program Sonu veya A - Başlatılan Bir Şey'in yürütülüp yürütülmeyeceğine dair bir garanti yoktur.
RiaanDP

@JimmyJimm: İş parçacığı kimliklerini göstermek için örneği güncelledim. Gördüğünüz gibi, "Program Sonu" ve "A - Bir şey başlattı" farklı iş parçacıkları üzerinde çalışıyor. Yani aslında düzen deterministik değildir.
Mas

10

Wait (), eşzamansız olarak eşzamansız kod çalıştırılmasına neden olur. beklemeyeceğim.

Örneğin, bir asp.net web uygulamanız var. UserA / getUser / 1 uç noktasını çağırır. asp.net uygulama havuzu, iş parçacığı havuzundan (Thread1) bir iş parçacığı seçer ve bu iş parçacığı bir http araması yapar. Wait () yaparsanız, http çağrısı çözülene kadar bu iş parçacığı engellenir. Beklerken, UserB / getUser / 2'yi çağırırsa, http çağrısını tekrar yapmak için uygulama havuzunun başka bir iş parçacığı (Thread2) sunması gerekir. Az önce (Eh, aslında uygulama havuzundan getirildi) başka bir iş parçacığı oluşturmadınız, çünkü Thread1'i kullanamıyorsunuz, Wait () tarafından engellendi.

Thread1'de await kullanırsanız, SyncContext Thread1 ve http çağrısı arasındaki senkronizasyonu yönetir. Basitçe, http çağrısı yapıldıktan sonra bunu bildirir. Bu arada, UserB / getUser / 2'yi çağırırsa, http çağrısı yapmak için tekrar Thread1'i kullanacaksınız, çünkü bir kez vurulduktan sonra serbest bırakılmıştır. Daha sonra başka bir istek daha da fazla kullanabilir. Http çağrısı yapıldığında (user1 veya user2), Thread1 sonucu alabilir ve arayan kişiye (istemci) geri dönebilir. Thread1 çoklu görevler için kullanıldı.


9

Bu örnekte, pratikte fazla değil. Farklı bir iş parçacığında geri dönen (WCF çağrısı gibi) bir Görev bekliyorsanız veya işletim sistemine (Dosya G / Ç gibi) denetimi bırakıyorsa, iş parçacığını engellemeyerek daha az sistem kaynağı kullanır.


3

Yukarıdaki örnekte, "TaskCreationOptions.HideScheduler" kullanabilir ve "DoAsTask" yöntemini büyük ölçüde değiştirebilirsiniz. "DoAsAsync" ile olduğu gibi yöntemin kendisi eşzamanlı değildir, çünkü bir "Görev" değeri döndürür ve "eşzamansız" olarak işaretlenir, birkaç kombinasyon yapar, bu bana "eşzamansız / beklemede" kullanmakla aynı şekilde verir :

static Task DoAsTask()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime, TaskCreationOptions.HideScheduler); //<-- HideScheduler do the magic

    TaskCompletionSource<int> tsc = new TaskCompletionSource<int>();
    t.ContinueWith(tsk => tsc.TrySetResult(tsk.Result)); //<-- Set the result to the created Task

    WriteOutput("2 - Task started");

    tsc.Task.ContinueWith(tsk => WriteOutput("3 - Task completed with result: " + tsk.Result)); //<-- Complete the Task
    return tsc.Task;
}
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.