2011-12-08 5 views
6

Każda wariacja na poniższym kodem, który staram nie działa - czy DoSomething() : void i nazywa się jak napisane lub DoSomething() : Task i nazywa się TaskEx.RunEx() niektóre próba z udziałem .GetAwaiter().GetResult(). Wyświetlane błędy: "Start may not be called on a task with null action", "RunSynchronously may not be called on a task unbound to a delegate" i "The task has not yet completed".Wywołanie metody asynchronicznej z metody non-asynchronicznym

class Program 
{ 
    static void Main(string[] args) // Starting from a non-async method 
    { 
     DoSomething(); 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static async void DoSomething() 
    { 
     Console.WriteLine("Starting DoSomething ..."); 

     var x = await PrepareAwaitable(1); 

     Console.WriteLine("::" + x); 

     var y = await PrepareAwaitable(2); 

     Console.WriteLine("::" + y); 
    } 

    static Task<string> PrepareAwaitable(int id) 
    { 
     return new Task<string>(() => 
     { 
      return "Howdy " + id.ToString(); 
     }); 
    } 
} 

wyjściowa:

Zaczynając doSomething ...

Naciśnij dowolny klawisz, aby zamknąć.

PrepareAwaitable 's Task' s Action będzie bardziej skomplikowane później. Kiedy ta czynność zostanie zakończona, jak długo potrwa, spodziewam się, że Task (lub inne mechanizmy ramowe) zostaną wznowione, przypisując "Howdy ..." do x, a następnie do y. NAPRAWDĘ chcę zrobić, to przechwycić oczekujące obiekty, przetworzyć je, a później, które kontroluję, wznowić z wynikiem (x i y). Ale nie byłem zbyt daleko na tym dużym kroku, więc staram się zacząć mniejszy.

+1

Co ty * próbujesz * zrobić? Trudno powiedzieć, co "nie działa", kiedy nie wiemy, jak "działa". –

+0

Chcę, aby przebiegał do końca, proszę. Być może wydawało mi się oczywiste, co powinienem robić z powodu tego, jak błędne jest moje zrozumienie. –

+0

@uosef: Uruchom co? Nie ma tu nic naprawdę asynchronicznego? –

Odpowiedz

5

Zadania, które powróciły, jeszcze się nie rozpoczęły (tj. Są "zimnymi" zadaniami); spróbuj zamienić kod PrepareAwaitable na:

static Task<string> PrepareAwaitable(int x) 
{ 
    return Task.Factory.StartNew<string>(() => 
    { 
     return "Howdy " + x.ToString(); 
    }); 
} 
+1

Hmm. To działa. Jak później można uruchomić zimne zadanie? Kiedy próbuję .Start() na oryginalnym kodzie (If DoSomething(): Task) pojawia się błąd "Start nie może zostać wywołany przy zadaniu z zerową akcją". –

5

To naprawdę nie jest jasne, co próbujesz osiągnąć, ponieważ nie ma nic asynchronicznego. Na przykład, będzie to skompilować i uruchomić, ale nie wiem, czy to, co chcesz:

using System; 
using System.Threading.Tasks; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     DoSomething(); 

     Console.WriteLine("Press any key to quit."); 
     Console.ReadKey(); 
    } 

    static async void DoSomething() 
    { 
     Console.WriteLine("Starting DoSomething ..."); 

     var x = await PrepareAwaitable(1); 

     Console.WriteLine("::" + x); 

     var y = await PrepareAwaitable(2); 

     Console.WriteLine("::" + y); 
    } 

    static async Task<string> PrepareAwaitable(int x) 
    { 
     return "Howdy " + x; 
    } 
} 

Zauważ, że to daje ostrzeżenie dla PrepareAwaitable bo nie ma nic asynchroniczne w nim; brak wyrażeń "oczekujących". Cały program wykonuje się synchronicznie. Inną alternatywą realizacja PrepareAwaitable:

static Task<string> PrepareAwaitable(int x) 
{ 
    return TaskEx.Run(() => "Howdy " + x); 
} 

Czy raczej to, czego szukali?

+1

Dzięki! TaskEx.Run() jest trasą, na którą liczyłem. Czy to jest lepsze niż 'Task.Factory.StartNew ()' CarlosFigueira? "Lepsze" oznacza zgodne z przeznaczeniem projektantów asynchronicznych. Które podejście ma mniej ograniczające konsekwencje, czy też są one synonimami? –

+1

@ uosɐs: Wierzę, że 'TaskEx.Run' jest głównie skrótem. Mogą istnieć pewne subtelne różnice, ale jeśli tak, to nie znam ich od ręki. –

+0

Dzięki za szczegółową odpowiedź! W miarę możliwości podzieliłbym punkty. –

10

Najpierw przeczytaj dokument Asynchronous Pattern. Jest pod numerem My Documents\Microsoft Visual Studio Async CTP\Documentation. W tym dokumencie opisano, jak projektować interfejsy API w sposób naturalny podlegające zużyciu przez await.

Po drugie, zdaj sobie sprawę, że istnieje kilka aspektów klasy Task i powiązanych interfejsów API, które nie mają już zastosowania w nowym świecie asynchronicznym. Task został pierwotnie napisany jako rdzeń licencji TPL. Okazało się być dobrym rozwiązaniem dla programowania asynchronicznego, więc Microsoft użył go zamiast tworzyć nową klasę "Obietnica". Ale wiele metod to zatrzymania, które są używane przez TPL, ale nie są potrzebne do programowania asynchronicznego.

Aby odpowiedzieć na tytułowe pytanie, mieszanie synchronicznego i asynchronicznego kodu nie jest całkiem proste z bieżącym asynchronicznym CTP. Napisałem a library, który zawiera metodę rozszerzenia Task.WaitAndUnwrapException, która ułatwia wywoływanie kodu asynchronicznego z kodu synchronizacji. Możesz być także zainteresowany moim AsyncContext class, który sprawia, że ​​monit "naciśnij dowolny klawisz" jest niepotrzebny.

+0

To naprawdę świetna odpowiedź. Ilustrujesz głębsze zrozumienie moich ogólnych zmagań z asynchronizmem, tak jak go uczyłem, i podzieliłbym punkty, gdybym mógł. –