Jak odkryłeś, w VS11 kompilator nie zezwala na async Main
metodę. Było to dozwolone (ale nigdy nie zalecane) w VS2010 z Async CTP.
Mam najnowsze posty na blogu dotyczące asynchronizacji / oczekiwania i w szczególności asynchronicznych programów konsolowych . Oto kilka podstawowych informacji z postu wprowadzającego:
Jeśli „oczekiwanie” zobaczy, że oczekiwane nie zostało zakończone, działa on asynchronicznie. Mówi oczekiwanemu o uruchomieniu pozostałej części metody po jej zakończeniu, a następnie powraca z metody asynchronicznej. Oczekuje również na przechwycenie bieżącego kontekstu, gdy przekaże pozostałą część metody do oczekiwanego.
Później, gdy oczekiwane zadanie się zakończy, wykona pozostałą część metody asynchronicznej (w przechwyconym kontekście).
Oto dlaczego jest to problem w programach konsoli z async Main
:
Pamiętaj z naszego wpisu wstępnego, że metoda asynchroniczna powróci do swojego obiektu wywołującego, zanim zostanie zakończona. Działa to doskonale w aplikacjach interfejsu użytkownika (metoda po prostu wraca do pętli zdarzeń interfejsu użytkownika) i aplikacjach ASP.NET (metoda zwraca wątek, ale utrzymuje żądanie przy życiu). Nie działa tak dobrze w przypadku programów konsolowych: Main powraca do systemu operacyjnego - więc program się kończy.
Jednym z rozwiązań jest zapewnienie własnego kontekstu - „głównej pętli” dla programu konsoli, która jest kompatybilna z asynchronicznie.
Jeśli masz komputer z Async CTP, możesz korzystać GeneralThreadAffineContext
z My Documents \ Microsoft Visual Studio Async CTP \ Samples (C # Testing) Testowanie jednostkowe \ AsyncTestUtilities . Alternatywnie możesz użyć AsyncContext
z mojego pakietu Nito.AsyncEx NuGet .
Oto przykład użycia AsyncContext
; GeneralThreadAffineContext
ma prawie identyczne zastosowanie:
using Nito.AsyncEx;
class Program
{
static void Main(string[] args)
{
AsyncContext.Run(() => MainAsync(args));
}
static async void MainAsync(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Alternatywnie możesz po prostu zablokować główny wątek konsoli, dopóki praca asynchroniczna nie zostanie zakończona:
class Program
{
static void Main(string[] args)
{
MainAsync(args).GetAwaiter().GetResult();
}
static async Task MainAsync(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Zwróć uwagę na użycie GetAwaiter().GetResult()
; pozwala to uniknąć AggregateException
owijania, które występuje, jeśli używasz Wait()
lub Result
.
Aktualizacja, 30.11.2017: Od wersji 3 programu Visual Studio 2017 (15.3) język obsługuje teraz async Main
- o ile zwraca Task
lub Task<T>
. Możesz teraz to zrobić:
class Program
{
static async Task Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = await bs.GetList();
}
}
Semantyka wydaje się być taka sama, jak GetAwaiter().GetResult()
styl blokowania głównego wątku. Jednak nie ma jeszcze specyfikacji języka dla C # 7.1, więc jest to tylko przypuszczenie.