2012-08-14 9 views
11

Mam dwie asynchroniczne metody Task<int> DoInt() i Task<string> DoString(int value). Mam trzecią metodę asynchroniczną Task<string> DoBoth(int value), której celem jest asynchroniczne wykonanie DoInt(), wyprowadzenie jej wyniku do DoString(int), a wynikiem jest wynik DoBoth().Kontynuowanie zadania do zadania bez blokowania wyniku. Wynik

Istotne ograniczenia są:

  1. może nie mam kodu źródłowego do DoInt() lub DoString(), więc nie można je modyfikować.
  2. Nie chcę, aby zablokować w dowolnym momencie
  3. Wyjście (wynik) z jednego zadania muszą być przekazywane jako wejście do następnego
  4. Wyjście końcowego (wynik) jest to, co chcę być wynikiem DoBoth()

Kluczem jest to, że mam już metody asynchroniczne (tj. Zadanie powrotu) i chcę przesyłać dane od zadania do zadania, bez blokowania po drodze, zwracając wynik końcowy w zadaniu początkowym. Mogę zrobić wszystkie poniższe wyjątkiem nie blokując w poniższym kodzie:

przykład

Kod:

// Two asynchronous methods from another library, i.e. can't be changed 
Task<int> DoInt(); 
Task<string> DoString(int value); 

Task<string> DoBoth() 
{ 
    return DoInt().ContinueWith<string>(intTask => 
    { 
     // Don't want to block here on DoString().Result 
     return DoString(intTask.Result).Result; 
    }); 
} 

Od Task<string> DoString(int) jest już sposób asynchroniczny, w jaki sposób czysto stworzyć nieblokującą kontynuację do niego? Skutecznie chcę utworzyć kontynuację z istniejącego zadania, a nie z func.

+1

Czy 'DoInt' przyjmuje int jako argument, czy nie? Masz to na dwa sposoby w swoim poście ... –

+0

@ReedCopsey - Poprawiłem podpis dla DoInt(). To, czy ma on parametr, czy nie, nie jest ważne w tym przykładzie, ale niekonsekwencja w moim przykładzie jest myląca :) Zmieniono także nazwę valueTask => intTask dla jasności. – MikeJansen

Odpowiedz

8

Można napisać cały łańcuch używając TaskExtensions.Unwrap jak:

Task<string> DoBoth(int value) 
{ 
    Task<Task<string>> task = DoInt(value).ContinueWith(valueTask => 
    { 
     return DoString(valueTask.Result); 
    }); 

    return task.Unwrap(); 
} 

pamiętać, że ta zakłada, że ​​DoInt jest zdefiniowany jako Task<int> DoInt(int value);, która różni się od opisu, ale podąża swoją przykład kodu.

Jeśli DoInt nie bierze int argument (dopasowanie swoich deklaracji), to może stać się:

Task<string> DoBoth() 
{ 
    return DoInt().ContinueWith(t => DoString(t.Result)).Unwrap(); 
} 

jako dodatkowy - przy użyciu C# 5 i .NET 4.5 (lub pakiet asynchroniczny kierowania) , możesz napisać to jako:

async Task<string> DoBoth(int value) 
{ 
    int first = await DoInt(value); 
    return await DoString(first); 
} 
+0

'valueTask.Result' nie będzie blokował, ale' DoString (valueTask.Result) .Result' będzie blokował aż do zakończenia zadania zwróconego przez 'DoString()'. I przez blokowanie nie mam na myśli tego, że pierwotny dzwoniący jest zablokowany; Mam na myśli tylko wątek, który tam siedzi potencjalnie bezczynnie czekając na kolejne zadanie do wykonania (w tym przypadku wątek uruchamia asynchronicznego delegata, ale nadal jest wątkiem z perspektywy zasobów systemowych). – MikeJansen

+0

Świetnie! Reed, gdybyś mógł to wyjaśnić; czy nie będzie blokowania, mimo że bierzesz 'valueTask.Result'? –

+1

@AndersGustafsson Nie - w momencie, w którym uzyskasz wynik, jesteś w kontynuacji. "valueTask" jest gwarantowane do ukończenia w tym momencie, więc '.Result' staje się nieblokujące. (Może jednak spowodować wyjątek, jeśli zadanie zostało anulowane lub się nie powiedzie, ale nigdy nie zostanie zablokowane.) –

Powiązane problemy