6

非同期で動作すると思っていたASP.NETWebAPIコントローラーがあります。コントローラは、最初の要求に対して20秒間スリープするように設計されていますが、後続の要求はすぐに処理します。したがって、私の予想されるタイムラインは次のようになります。

  1. リクエスト1を発生させます。
  2. リクエスト2を発生させます。
  3. リクエスト3を発生させます。
  4. リクエスト2が返されます。
  5. リクエスト3が返されます。
  6. 約20秒待ちます。
  7. リクエスト1が返されます。

代わりに、リクエスト1が終了するまでリクエストは返されません。

(デバッグ出力に基づいて)エントリスレッドとスリープスレッドIDが異なることを確認できます。私は意図的TaskCreationOptions.LongRunningにスリープを別のスレッドに強制するために使用しましたが、それでもアプリケーションは、そのスリープが終了するまで新しい要求を処理することを拒否します。

非同期WebAPIコントローラーが実際にどのように機能するかについて基本的なことを見逃していますか?


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はどちらも、最初のリクエストが応答するまで、「重複した」リクエストと見なされるものを送信したくないようです。いずれかのブラウザの個別の「プライベート」セッションは、2番目のリクエストからすぐに戻ります。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 に答える