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

时间:2013-02-21 07:13:31

标签: c# asp.net-web-api async-await

我有一个ASP.NET Web API控制器,我将 think 异步操作。控制器设计为第一次请求休眠20秒,但立即为任何后续请求提供服务。所以我的预期时间表就像是:

  1. 提出请求1.
  2. 提出请求2。
  3. 提出要求3。
  4. 请求2返回。
  5. 请求3返回。
  6. 等待~20秒。
  7. 请求1返回。
  8. 相反,没有请求返回,直到请求1完成。

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

    我是否遗漏了有关异步Web API控制器如何工作的基本信息?


    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);
        }
    }
    

2 个答案:

答案 0 :(得分:12)

这实际上与服务器无关,而且与客户端有关。 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);
    }
}

输出结果如下:

  1. 客户端2获取数据:“FOOBAR”(在启动的毫秒内)
  2. 客户3获得了数据:“FOOBAR”
  3. 任务2先完成
  4. (漫长的等待)
  5. 客户1获得了数据:“FOOBAR”
  6. 所有任务已完成

答案 1 :(得分:0)

在我的情况下输出(切换到另一个线程从5到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).

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