6

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

  1. 提出请求 1。
  2. 提出请求 2。
  3. 提出要求 3。
  4. 请求 2 返回。
  5. 请求 3 返回。
  6. 等待约 20 秒。
  7. 请求 1 返回。

相反,在请求 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);
    }
}
4

2 回答 2

13

这实际上与服务器无关,与客户端有关。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. 所有任务都已完成
于 2013-02-21T08:22:06.003 回答
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).

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

于 2013-02-21T10:26:11.140 回答