2013-02-21 14 views
6

Mam kontrolera ASP.NET Web API, który miałbym , że będzie działać asynchronicznie. Kontroler jest przeznaczony do snu 20 sekund na pierwsze żądanie, ale natychmiast obsługuje kolejne żądania. Więc moje Przewiduje timeline byłoby coś takiego:Dlaczego mój asynchroniczny kontroler ASP.NET Web API blokuje główny wątek?

  1. Raise życzenie 1.
  2. prośba Podnieś 2.
  3. żądania Raise 3.
  4. życzenie 2 powroty.
  5. Żądanie 3 zwraca.
  6. Czekaj ~ 20 sekund.
  7. Żądanie 1 zwraca.

Zamiast tego, no żąda powrotu, aż żądanie 1 zostanie zakończone.

Mogę potwierdzić (na podstawie wyników debugowania), że wątek wejściowy i identyfikator wątku spoczynkowego są różne. Celowo użyłem TaskCreationOptions.LongRunning, aby zmusić sen do oddzielnego wątku, ale mimo to aplikacja odmawia obsługi nowych żądań, dopóki ten sen się nie skończy.

Czy brakuje mi czegoś fundamentalnego o tym, jak naprawdę działają kontrolery Web API async?


public class ValuesController : ApiController 
{ 
    private static bool _firstTime = true; 

    public async Task<string> Get() 
    { 
     Debug.WriteLine("Entry thread id: {0}. Sync: {1}", 
      Thread.CurrentThread.ManagedThreadId, 
      SynchronizationContext.Current); 
     await LongWaitAsync(); 
     return "FOOBAR"; 
    } 

    private Task LongWaitAsync() 
    { 
     return Task.Factory.StartNew(() => 
      { 
       if (_firstTime) 
       { 
        _firstTime = false; 
        Debug.WriteLine("Sleepy thread id: {0}. Sync: {1}", 
         Thread.CurrentThread.ManagedThreadId, 
         SynchronizationContext.Current); 
        Thread.Sleep(20000); 
        Debug.WriteLine("Finished sleeping"); 
       } 
      }, 
      CancellationToken.None, 
      TaskCreationOptions.LongRunning, 
      TaskScheduler.Default); 
    } 
} 
+0

Pierwszym krokiem byłoby oznaczenie '_firstTime' jako' volatile', aby upewnić się, że oddzielne wątki mogą widzieć, że zmienia się konsekwentnie. –

+0

Jakiego serwera internetowego używasz do hostowania? –

+0

Oznaczenie '_firstTime' jako' volatile' nie ma znaczenia. Próbowałem już hostingu w IIS Express 8.0 i IIS 7.5, zarówno na Win 7 Pro. – Snixtor

Odpowiedz

12

To faktycznie nie ma nic wspólnego z serwerem, a wszystko, co zrobić z klientem. Wygląda na to, że zarówno Chrome, jak i Firefox nie chcą wysyłać tego, co uważają za "duplikat", dopóki pierwsza nie otrzyma odpowiedzi. Oddzielna "prywatna" sesja dowolnej przeglądarki natychmiast zwróci się z drugiego żądania. Internet Explorer 9 wydaje się nie wykazywać takiego zachowania.

Aby odizolować od implementacji klienta, połączyłem następującego klienta.

class Program 
{ 
    static void Main(string[] args) 
    { 
     var t1 = Task.Run(() => FetchData(1)); 
     var t2 = Task.Run(() => FetchData(2)); 
     var t3 = Task.Run(() => FetchData(3)); 

     var index = Task.WaitAny(t1, t2, t3); 
     Console.WriteLine("Task {0} finished first", index + 1); 

     Task.WaitAll(t1, t2, t3); 
     Console.WriteLine("All tasks have finished"); 

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

    static void FetchData(int clientNumber) 
    { 
     var client = new WebClient(); 
     string data = client.DownloadString("http://localhost:61852/api/values"); 
     Console.WriteLine("Client {0} got data: {1}", clientNumber, data); 
    } 
} 

jest wyjście idzie on:

  1. Client 2 got danych: "foobar" (w ciągu milisekund starcie)
  2. Client 3 dostali dane: "blabla"
  3. zadanie 2 zakończył pierwszy
  4. (długo czekać tutaj)
  5. Client 1 dostaje dane: "blabla"
  6. wszystkich t zwraca zakończeniu
+0

Myślałem, że zwariowałem. Dzięki za objawienie! – NovaJoe

0

W moim przypadku tutaj jest wyjście (IT przełączniki do innego wątku od 5 do 10):

Entry thread id: 5. Sync: System.Web.LegacyAspNetSynchronizationContext 
Sleepy thread id: 10. Sync: 
Finished sleeping 
The thread '<No Name>' (0x2c84) has exited with code 0 (0x0). 
Entry thread id: 7. Sync: System.Web.LegacyAspNetSynchronizationContext 
The thread '<No Name>' (0x1818) has exited with code 0 (0x0). 
Entry thread id: 5. Sync: System.Web.LegacyAspNetSynchronizationContext 
The thread '<No Name>' (0xd4c) has exited with code 0 (0x0). 
The thread '<No Name>' (0x2c30) has exited with code 0 (0x0). 

Może to być spowodowane środowiska i komputerze z systemem (single core), które sprawia, że ​​środowisko wykonawcze decyduje, jak go uruchomić.

Powiązane problemy