4

HttpClient を同期的に使用しようとしていますが、多くの同時リクエストを行うと機能しなくなります。非同期使用用と同期使用用の 2 つのテストを作成しました。TestMethod常に 4 秒後に応答を返します。非同期テストは正常に動作します。同期テストではほとんどすべてのリクエストがタイムアウトになり、最後のリクエストは最大 20 件しか成功しません。単一のリクエストを使用して、新しいリクエストごとにHttpClient新しいインスタンスを作成しようとしました。HttpClient変わりはない。ひょっとしたら、このデッドロックが関係しているのかもしれません。

Framework 4.5 を対象とする .NET Framework 4.5.1 で VS2013 を使用しています。NuGet 経由で HttpClient を取得します。<package id="Microsoft.Net.Http" version="2.2.15" targetFramework="net45" />

HttpClientアプリ全体を書き直さなければならないことを意味するため、まだ非同期で使用したくありません。ここで何が間違っているのですか?

// all 800 requests complete successfully
[Test]
public async void Asyncmethod()
{
    var sw = new Stopwatch();
    sw.Start();

    var client = new HttpClient();
    client.Timeout = TimeSpan.FromSeconds(15);

    var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(async () =>
    {
        try
        {
            var swx = new Stopwatch();
            swx.Start();
            var response = await client.GetStringAsync("http://localhost:7002/TestMethod").ConfigureAwait(false);
            swx.Stop();
            Console.WriteLine(x + " " + response + " in " + swx.ElapsedMilliseconds + " ms.");
        }
        catch (Exception e)
        {
            Console.WriteLine(x + " Exception: " + e.Message);
        }
    })).ToArray();

    await Task.WhenAll(tasks);

    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
}

// almost all of 800 requests time out
[Test]
public void Syncmethod()
{
    var sw = new Stopwatch();
    sw.Start();

    var client = new HttpClient();

    var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(() =>
    {
        try
        {
            var swx = new Stopwatch();
            swx.Start();
            var response = client.GetStringAsync("http://localhost:7002/TestMethod");
            if (response.Wait(15000))
            {
                swx.Stop();
                Console.WriteLine(x + " " + response.Result + " in " + swx.ElapsedMilliseconds + " ms.");
            }
            else
            {
                swx.Stop();
                Console.WriteLine(x + " timed out.");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(x + " Exception: " + e.Message);
        }
    })).ToArray();

    foreach (var task in tasks)
        task.Wait(60000);

    sw.Stop();
    Console.WriteLine(sw.ElapsedMilliseconds);
}
4

2 に答える 2

2

はい、デッドロックのようです。

このコードは完璧に機能します

public static void Syncmethod()
    {
        var sw = new Stopwatch();
        sw.Start();

        var client = new HttpClient();

        var tasks = Enumerable.Range(0, 800).Select(x => Task.Run(() =>
        {

            var swx = new Stopwatch();
            swx.Start();
            var result =
            client.GetStringAsync("http://yandex.ru").ContinueWith(task =>
            {
                try
                {
                    swx.Stop();
                    Console.WriteLine(x + " " + task.Result + " in " + swx.ElapsedMilliseconds + " ms.");
                    return task.Result;
                }
                catch (Exception e)
                {
                    swx.Stop();
                    Console.WriteLine(x + " Exception: " + e.Message);
                    throw e;
                }
            }, TaskContinuationOptions.AttachedToParent);

        })).ToArray();

        foreach (var task in tasks)
            task.Wait(60000);

        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds);
    }

タイムアウトを処理するには、追加のロジックが必要だと思います。たとえば、15 秒で完了するもう 1 つのタスクなどです。

この投稿を見てくださいHttpClient.GetAsync(...) never return when using await/async

上記のように、単純な変更で非同期の例をハングさせることができます

var response = client.GetStringAsync("http://yandex.ru").GetAwaiter().GetResult();

非同期コードを記述するときは、ブロック実行を決して使用せず、結果を待ちます。

于 2013-10-22T08:46:14.890 に答える