7

HttpListener同時に複数の要求を処理するために、次の単純なものを作成しました(.NET 4.5 上)。

class Program {

    static void Main(string[] args) {

        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://+:8088/");
        listener.Start();
        ProcessAsync(listener).ContinueWith(task => { });
        Console.ReadLine();
    }

    static async Task ProcessAsync(HttpListener listener) {

        HttpListenerContext ctx = await listener.GetContextAsync();

        // spin up another listener
        Task.Factory.StartNew(() => ProcessAsync(listener));

        // Simulate long running operation
        Thread.Sleep(1000);

        // Perform
        Perform(ctx);

        await ProcessAsync(listener);
    }

    static void Perform(HttpListenerContext ctx) {

        HttpListenerResponse response = ctx.Response;
        string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
        byte[] buffer = Encoding.UTF8.GetBytes(responseString);

        // Get a response stream and write the response to it.
        response.ContentLength64 = buffer.Length;
        Stream output = response.OutputStream;
        output.Write(buffer, 0, buffer.Length);

        // You must close the output stream.
        output.Close();
    }
}

Apache Benchmark Tool を使用して、これを負荷テストします。1 リクエストを行うと、リクエストの最大待機時間が 1 秒になります。たとえば、10 個のリクエストを行うと、応答の最大待機時間は最大 2 秒になります。

上記のコードを可能な限り効率的にするには、どのように変更しますか?

編集

@JonSkeetの回答の後、コードを次のように変更しました。最初に、ブロッキング呼び出しをシミュレートしようとしましたが、それがコアの問題だったと思います。だから、私は@JonSkeetの提案を取り、それをTask.Delay(1000)に変更しました。さて、以下のコードは最大を与えます。待ち時間はおおよそです。10 個の同時リクエストで 1 秒:

class Program {

    static bool KeepGoing = true;
    static List<Task> OngoingTasks = new List<Task>();

    static void Main(string[] args) {

        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://+:8088/");
        listener.Start();
        ProcessAsync(listener).ContinueWith(async task => {

            await Task.WhenAll(OngoingTasks.ToArray());
        });

        var cmd = Console.ReadLine();

        if (cmd.Equals("q", StringComparison.OrdinalIgnoreCase)) {
            KeepGoing = false;
        }

        Console.ReadLine();
    }

    static async Task ProcessAsync(HttpListener listener) {

        while (KeepGoing) {
            HttpListenerContext context = await listener.GetContextAsync();
            HandleRequestAsync(context);

            // TODO: figure out the best way add ongoing tasks to OngoingTasks.
        }
    }

    static async Task HandleRequestAsync(HttpListenerContext context) {

        // Do processing here, possibly affecting KeepGoing to make the 
        // server shut down.

        await Task.Delay(1000);
        Perform(context);
    }

    static void Perform(HttpListenerContext ctx) {

        HttpListenerResponse response = ctx.Response;
        string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
        byte[] buffer = Encoding.UTF8.GetBytes(responseString);

        // Get a response stream and write the response to it.
        response.ContentLength64 = buffer.Length;
        Stream output = response.OutputStream;
        output.Write(buffer, 0, buffer.Length);

        // You must close the output stream.
        output.Close();
    }
}
4

1 に答える 1

7

リスナーの分岐で終わるように私には見えます。内で、(Task.Factory.StartNew を介して) リッスンする新しいタスクを開始し、メソッドの最後で再度ProcessAsync呼び出します。それはどうやって終わらせることができますか?それがパフォーマンスの問題の原因であるかどうかは明らかではありませんが、一般的には間違いなく問題のようです.ProcessAsync

コードを単純なループに変更することをお勧めします。

static async Task ProcessAsync(HttpListener listener) {
    while (KeepGoing) {
        var context = await listener.GetContextAsync();
        HandleRequestAsync(context);         
    }
}

static async Task HandleRequestAsync(HttpListenerContext context) {
    // Do processing here, possibly affecting KeepGoing to make the 
    // server shut down.
}

現在、上記のコードは の戻り値を無視していますHandleRequestAsync「現在進行中」のタスクのリストを保持したい場合があります。また、シャットダウンを求められたときにawait Task.WhenAll(inFlightTasks)、サーバーをすぐにダウンさせないようにするために使用します。

Thread.Sleepまた、ブロッキング遅延であることに注意してください。非同期遅延はawait Task.Delay(1000).

于 2012-12-04T19:04:35.660 に答える