2016-04-28 8 views
7

Uczę się, jak korzystać z funkcji asynchronicznych w aplikacji konsoli, ale nie można wykonać zadania. Poczekaj, aż wszystkie zadania zostaną zakończone. Co jest nie tak z następującym kodem? Działa synchronicznie. Z góry dziękuję.Task.WhenAll nie czekając

static void Main(string[] args) 
{ 
    ... 
    IncluiValores(...); 
    ... 
} 

static async void IncluiValores(...) 
{ 
    Task<List<int>> res1 = att.GetAIDBAPI(att); 
    Task<List<int>> res2 = att.GetAIDBAPI(att2); 

    List<int>[] res = await Task.WhenAll(res1, res2); 

    ... 
} 

AKTUALIZACJA - Funkcja Definicja:

public async Task<List<int>> GetAIDBAPI(Attributes attributes) 
    { 

     List<int> results = null; 

     Connections client0 = new Connections(); 
     HttpClient client = client0.OpenAPIConnection(attributes.User[0], attributes.Pwd, attributes.Server, attributes.Chave, attributes.Server2); 
     HttpResponseMessage response = await client.PostAsJsonAsync("api/Attributes/ID/Bulk", attributes); 

     if (response.IsSuccessStatusCode) 
     { 
      var content = await response.Content.ReadAsStringAsync(); 
      results = JsonConvert.DeserializeObject<dynamic>(content).ToObject<List<int>>(); 
     } 
     else 
     { 
      var content = "[{-1}]"; 
      var result = JsonConvert.DeserializeObject<dynamic>(content); 
      results = result.ToObject<List<int>>(); 
     } 

     return results; 

    } 

UPDATE 2 - Oddzielna Kontekst

static void Main(string[] args) 
{ 
    AsyncContext.Run(() => MainAsync(args)); 
} 

static async void MainAsync(string[] args) 
{ 
    await IncluiValores(...); 
} 

static async Task IncluiValores(...) 
{ 
    Task<List<int>> res1 = att.GetAIDBAPI(att); 
    Task<List<int>> res2 = att.GetAIDBAPI(att2); 

    List<int>[] res = await Task.WhenAll(res1, res2); // <- Error here 
    //Collection was modified; enumeration operation may not execute 
    ... 
} 
//Tried to change to code below but it does not wait. 
static async Task IncluiValores(...) 
{ 
    Task<List<int>> res1 = att.GetAIDBAPI(att); 
    Task<List<int>> res2 = att.GetAIDBAPI(att2); 

    await Task.WhenAll(res1, res2); // <- No error, just doesn't wait. 
    list.Add(res1.Result[0]); 
} 
+0

Jak wygląda definicja GetAIDBAPI? – BoltClock

+0

Definicja wliczona w cenę. – Gabriel

+0

Gdzie jest twój kod telefoniczny? Jeśli jest w Main, nie będzie działać poprawnie. Jeśli jest to we własnej asynchronicznej metodzie, jak uruchamiane jest zadanie dla tej metody? – BoltClock

Odpowiedz

4

Dzwonisz metodę async void, która nieodłącznie oznacza, że ​​nie trzeba sposobem await ing wynik. Ilekroć pomijasz await, przerywasz łańcuch synchronizacji. Operacja odbywa się naprawdę asynchronicznie, zamiast "resynchronizować" przez await. Kontrola jest zwracana do osoby dzwoniącej, natomiast (czasami w przyszłości) operacja jest wznawiana asynchronicznie.

Pamiętaj, await to return. To tylko konsekwentne korzystanie z await zapewnia synchronizację. Przestań używać async void - zmień go na async Task i upewnij się, że prawidłowo uzyskałeś wynik. To samo dotyczy metody MainAsync. Task to void metod asynchronicznych.

Jest tylko jeden przypadek, w którym powinieneś zobaczyć async void, i jest to w kontekście synchronizacji starszych programów obsługi zdarzeń (np. W WinForm). Jeśli jest możliwe, aby metoda async zwróciła wartość Task, naprawdę powinna. Nie łamać łańcucha.

2

Błąd polega na tym, że twoja główna funkcja nie czeka na zakończenie procedury IncluiValores. Twój główny program kończy się przed zakończeniem procedury IncluiValores.

Z powodu tego błędu zakładam, że nadal masz problemy ze zrozumieniem, co się dzieje, gdy używasz async-await.

Ktoś tutaj na StackOverflow (niestety już nie mogę go znaleźć), wyjaśnił mi to, używając następującej metafory.

Ponadto: Znalazłem metafora
It is in this interview with Eric Lippert
Szukaj gdzieś w środku dla async-czekają
Koniec Adition

Załóżmy, że trzeba zrobić śniadanie. Chcesz wznieść trochę chleba, ugotować jajka i zrobić herbatę.

Synchronous

  • Put chleb w tosterze i czekać, aż chleb tostowy
  • Wyjąć chleb z tostera.
  • start wrzątku, poczekać aż woda wrze
  • Umieść jajka w wrzącej wody i czekać 7 minut, aż jajka są gotowe
  • Wyjąć jaja z wody
  • start wrzącej wody na herbatę i poczekać, aż woda wrze
  • Gdy woda wrze umieścić go w imbryku i dodać kilka liści herbaty i odczekać 4 minuty
  • Wreszcie masz wszystko razem i dostosowania go do stolika śniadaniowej.

Widzisz, że dużo czekasz, co jest stratą czasu, nie wspominając o tym, że Twój chleb jest prawdopodobnie zimny przed zakończeniem parzenia herbaty.

Byłoby o wiele bardziej efektywne, jeśli nie czekać przez cały czas, ale zaczną rzeczy jednocześnie

użyciu asynchronicznej-czekają: Asynchroniczny stosując jeden wątek

  • rozpocząć się w synchroniczny przypadek: włóż chleb do tostera
  • , ale teraz nie czekasz, aż chleb zostanie wzniesiony. Pamiętaj, co powinieneś zrobić, gdy chleb jest opiekany (pamiętaj o tym jako Zadanie A).
  • Zacznij gotować wodę, ale nie czekaj, aż woda zagotuje się. Pamiętaj, co powinieneś zrobić, gdy woda się zagotuje (pamiętaj o tym jako Zadanie B).
  • Zacznij gotować wodę na herbatę, ale nie czekaj, aż kelner się zagotuje. Pamiętaj, co powinieneś zrobić, gdy czajnik się zagotuje (pamiętaj o tym jako Zadanie C)

  • Poczekaj, aż zakończy się jedno z zadań A/B/C. Kontynuuj, co pamiętałeś, co powinieneś zrobić po zakończeniu zadania. Jeśli to wymaga innego czekania (czas na jajka lub herbatę być gotowym), nie czekaj na to, ale pamiętaj o zadaniach D i E i zaczekaj na wszystkie nie zakończone zadania.

Należy zauważyć, że w tej metodzie nadal jest wykonywana tylko jedna osoba. Jeśli korzystasz z async-await w ten sposób, jest zaangażowany tylko jeden wątek. Ten wątek tylko czeka, jeśli naprawdę nie ma nic do zrobienia. Zaletą tego jest to, że nie napotykasz problemów, które zwykle napotykasz podczas używania kilku wątków.

asynchroniczny przy użyciu kilku wątków

Można zatrudnić kilku kucharzy: jeden na chleb tostowy i jeden gotować jajka podczas dokonać tead. Jest to droga metoda: rozpocznij kilka wątków, podczas gdy wątki robią tylko czekanie przez większość czasu. Masz również problemy, które muszą zsynchronizować ci trzej kucharze, aby upewnić się, że nie korzystają jednocześnie z pieca kominkowego.

Stephen Cleary napisał obszerny artykuł opisujący to zachowanie async-czekają w Async and Await (Dziękuję Stephen!)

static void Main(string[] args) 
{ 
    var breakFast = await Task.Run(() => MakeBreakFast()); 
    // once here I know breakfast is ready 
    Eat(breakFast); 
} 
private static async Task<BreakFast> MakeBreakFast() 
{ 
    var taskToastBread = ToastBreadAsync(); 
    // do not await. As soon as the procedure awaits come back to do the next statement: 
    var taskBoilEggs = BoilEggsAsync(); 
    // again do not await. Come back as the procedure awaits 
    var taskMakeTea = MakeTeaAsync(); 
    // do not wait, but come bask as soon as the procedure await 

    // now wait until all three tasks are finished: 
    await Task.WhenAll (new Task[] {taskToasBread, taskBoilEggs, taskMakeTea}); 
    // if here: all tasks are finished. Property Result contains the return value of the Task: 
    return new BreakFast() 
    { 
     Toast = taskToastBread.Result, 
     Eggs = taskBoilEggs.Result, 
     Tea = taksMakeTea.Result, 
    } 
} 

private static Task<Toast> ToastBreadAsync() 
{ 
    var sliceOfBread = Loaf.CutSliceOfBread(); 
    Toaster.Insert(sliceOfBread); 
    await Toaster.Toast(); 
    // the function does not wait but return to the caller. 
    // the next is done when the caller await and the toaster is ready toasting 
    var toast = Toaster.Remove(); 
    return Toast(); 
} 

private static Task<Eggs> BoilEggsAsync() 
{ 
    var eggPan = ... 
    await eggPan.BoilWater(); 
    var eggs = Fridge.ExtreactEggs(); 
    EggPan.Insert(eggs); 
    await Task.Delay(TimeSpan.FromMinutes(7)); 
    return EggPan.Remove(); 
} 

Pewnie wiesz już jak zrobić herbatę.

+0

Czytałem tę kopię wklejoną "robiąc śniadanie", tak wiele razy, czuję, że wiem absolutnie * wszystko * co trzeba wiedzieć o opiekaniu chleba i gotowaniu jajek (oczywiście równolegle). –

+0

Podobne pytania prowadzą do podobnych odpowiedzi. Widzę dość często ludzi borykających się z koncepcją poczęcia. Najwyraźniej stare odpowiedzi nie są wystarczająco jasne lub nie można ich znaleźć. Chociaż nie byłam pierwszą osobą, której powiedziano, naprawdę pomogła mi zrozumieć, co się stało. Przy okazji: czy widziałeś, że przykład się poprawia? –

Powiązane problemy