2012-10-23 9 views
7

Chciałbym poznać uzasadnienie sposobu, w jaki kompilator wybiera TaskScheduler podczas kompilacji za pomocą asynchronicznego słowa kluczowego.asynchroniczne słowo kluczowe i wybór TaskScheduler

Moja metoda testowa jest wywoływana przez SignalR (host ASP.NET, IIS8, transport websocket) w metodzie OnConnectedAsync.

protected override async Task OnConnectedAsync(IRequest request, string connectionId) 
{ 
    SendUpdates(); 
} 

Rozpoczynanie zadania w bieżącym kontekście synchronizacji doprowadzi do InvalidOperationException w System.Web.AspNetSynchronizationContext.OperationStarted()

asynchroniczny operacja nie może być uruchomiona w tym czasie. Operacje asynchroniczne można uruchamiać tylko w asynchronicznym programie obsługi lub module lub podczas określonych zdarzeń w cyklu życia strony. Jeśli ten wyjątek wystąpił podczas wykonywania strony, upewnij się, że strona jest oznaczona jako <%@ Page Async="true" %>.

Dobrze. Za pomocą tej definicji SendUpdates otrzymuję powyższy wyjątek:

private async void SendUpdates() 
    { 
     Task.Run(async() => 
      { 
       while (true) 
       { 
        await Task.Delay(1000); 
        await Connection.Broadcast("blabla"); 
       } 
      }); 

    } 

Ale jeszcze bardziej interesująca jest sytuacja, gdy nie otrzymuję wyjątku. Następujące prace:

private void SendUpdates() 

oraz następujące prace zbyt

private async Task SendUpdates() 

ten ostatni działa zbyt, ale to w zasadzie takie same jak w powyższym przykładzie.

private Task SendUpdates() 
    { 
     return Task.Run(async() => 
      { 
       while (true) 
       { 
        await Task.Delay(1000); 
        await Connection.Broadcast("blabla"); 
       } 
      }); 

    } 

Czy wiesz, w jaki sposób kompilator wybiera program do planowania?

Odpowiedz

10

Jednym z podstawowych wytycznych w pisaniu kodu async jest „uniknąć async void” - to znaczy, należy async Task zamiast async void chyba że wdrożenie obsługi async zdarzeń.

async void metody używają SynchronizationContext 's OperationStarted i OperationCompleted; zobacz mój artykuł MSDN It's All about the SynchronizationContext po więcej szczegółów.

ASP.NET wykrywa wywołanie OperationStarted i (słusznie), odrzuca go, ponieważ jest to niezgodne z prawem, aby umieścić tam procedurę obsługi async zdarzeń. Po poprawieniu kodu do użycia async Task, ASP.NET nie widzi już obsługi zdarzeń async.

Może mi się przydać pomocnika intro to async/await post.

+0

To jest odpowiedź, dzięki – Xin

3

Kiedy zadzwonić:

private async void SendUpdates() 

Przy wywołaniu Task.Run i używając async keyword na anonimowego delegata, nie są faktycznie zapewnienie kontynuacji; uruchamiasz Task, a ty nadajesz kontynuację metodzie Run, którą następnie przetwarza. Ta kontynuacja nie jest przekazywana w żaden sensowny kod, który nazywa się Task.Run.

Dlatego masz wyjątek, przewodnik nie wie do await na Task że wezwanie do Task.Run produkuje.

Powiedział:

private void SendUpdates() 

działa, ponieważ zadanie jest tworzone i kod nie uchwycić SynchronizationContext (bo nie ma async kluczowe na metodzie Task przypadki nie uchwycić go domyślnie) . Strzelasz z tego zadania, ale to jest ogień-i-zapomnij.

I dodaje działa zbyt:

private async Task SendUpdates() 

Mianowicie ponieważ w zwrocie Task, masz zwrócony awaitable że zwrotna może pracować.

Aby odpowiedzieć bezpośrednio na pytanie, kompilator sprawi, że SynchronizationContext powróci z SynchronizationContext.Current, zanim zadzwonisz pod numer await; jakakolwiek kontynuacja jest wywoływana po tym, jak wygasną zwroty będą wywoływane przy użyciu tego SynchronizationContext.

+0

W rzeczywistości nie chcę niczego czekać. Czy masz na myśli to, że kiedy piszę async Task SendUpdates(), moje wewnętrzne zadanie jest opakowane w Zadanie kompatybilne z kontekstem synchronizacji ASP.NET, ale kiedy wywołuję async void SendUpdates(), to owijanie się nie dzieje, co wyjaśnia problem? – Eilistraee

+0

@Eilistraee Kiedy piszesz 'async Task SendUpdates', tak,' SynchronizationContext' jest przechwytywane i oczekiwane jest 'Task', które jest zwracane. Kiedy masz 'async void SendUpdates', nie oczekujesz na nic. Jeśli cokolwiek, powinieneś mieć przynajmniej ostrzeżenie kompilatora wskazujące, że używasz 'async', ale nie masz odpowiadającego' await' ('await' jest na anonimowym delegacie, a nie w faktycznym kodzie wywołania metody). – casperOne

+0

Ale nawet w przypadku asynchronicznego zadania SendUpdates() nie czekam na zwrócone zadanie. Chodzi o to, że to odniesienie do zadania zostało utracone, a OnConnectedAsync kontynuuje jego wykonywanie bez oczekiwania na nic (i dobrze, że wewnętrzne zadanie nie zostanie ukończone) – Eilistraee