C# 为什么我的异步ASP.NET Web API控制器会阻止主线程?

C# 为什么我的异步ASP.NET Web API控制器会阻止主线程?,c#,asp.net-web-api,async-await,C#,Asp.net Web Api,Async Await,我有一个ASP.NET Web API控制器,我本以为它会异步运行。控制器设计为在第一次请求时休眠20秒,但立即为任何后续请求提供服务。因此,我预期的时间线如下: 提出请求1 提出请求2 提出请求3 请求2返回 请求3个返回 等待约20秒 请求1返回 相反,no请求返回,直到请求1完成 我可以确认(基于调试输出),入口线程和休眠线程id是不同的。我故意使用TaskCreationOptions.LongRunning将睡眠强制到一个单独的线程上,但在睡眠完成之前,应用程序仍然拒绝服务任何新请求

我有一个ASP.NET Web API控制器,我本以为它会异步运行。控制器设计为在第一次请求时休眠20秒,但立即为任何后续请求提供服务。因此,我预期的时间线如下:

  • 提出请求1
  • 提出请求2
  • 提出请求3
  • 请求2返回
  • 请求3个返回
  • 等待约20秒
  • 请求1返回
  • 相反,no请求返回,直到请求1完成

    我可以确认(基于调试输出),入口线程和休眠线程id是不同的。我故意使用
    TaskCreationOptions.LongRunning
    将睡眠强制到一个单独的线程上,但在睡眠完成之前,应用程序仍然拒绝服务任何新请求

    我是否遗漏了异步Web API控制器如何真正工作的一些基本信息


    公共类值控制器:ApiController
    {
    私有静态bool_firstTime=true;
    公共异步任务Get()
    {
    WriteLine(“入口线程id:{0}.Sync:{1}”,
    Thread.CurrentThread.ManagedThreadId,
    SynchronizationContext.Current);
    等待LongWaitAsync();
    返回“FOOBAR”;
    }
    私有任务LongWaitAsync()
    {
    返回Task.Factory.StartNew(()=>
    {
    如果(第一次)
    {
    _第一次=错误;
    WriteLine(“休眠线程id:{0}.Sync:{1}”,
    Thread.CurrentThread.ManagedThreadId,
    SynchronizationContext.Current);
    睡眠(20000);
    Debug.WriteLine(“已完成睡眠”);
    }
    },
    取消令牌。无,
    TaskCreationOptions.LongRunning,
    TaskScheduler.Default);
    }
    }
    
    这实际上与服务器无关,而与客户端有关。Chrome和Firefox似乎都不想发送他们认为是“重复”的请求,直到第一个请求得到响应。两个浏览器的单独“专用”会话将立即从第二个请求返回。Internet Explorer 9似乎没有表现出这种行为

    为了与客户机实现隔离,我将以下客户机放在一起

    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);
        }
    }
    
    它的输出是:

  • 客户端2获得数据:“FOOBAR”(启动后的毫秒内)
  • 客户3得到数据:“FOOBAR”
  • 任务2首先完成
  • (在这里等了很久)
  • 客户端1获得数据:“FOOBAR”
  • 所有任务都已完成
  • 在我的例子中,这里是输出(它从5到10切换到另一个线程):

    入口线程id:5。Sync:System.Web.legacyapsnetsynchronizationcontext
    休眠线程id:10。同步:
    睡完了
    线程“”(0x2c84)已退出,代码为0(0x0)。
    入口线程id:7。Sync:System.Web.legacyapsnetsynchronizationcontext
    线程“”(0x1818)已退出,代码为0(0x0)。
    入口线程id:5。Sync:System.Web.legacyapsnetsynchronizationcontext
    线程“”(0xd4c)已退出,代码为0(0x0)。
    线程“”(0x2c30)已退出,代码为0(0x0)。
    

    这可能是由于环境和机器运行(单核)使得运行时决定如何运行它。

    作为第一步,我将
    \u firstTime
    标记为
    volatile
    ,确保不同的线程可以看到它不断变化。您使用哪个web服务器作为主机?将
    \u firstTime
    标记为
    volatile
    没有区别。我尝试过在Win 7 Pro上使用IIS Express 8.0和IIS 7.5托管。@Snixtor-这可能没有什么区别,但无论如何都应该这样做。@Damien_不信者适时指出。我承认我必须查找它的含义,它肯定与我的示例代码有关。我想我疯了。谢谢你的启示!
    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);
        }
    }
    
    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).