【解決方法】C# の task.waitasync でスレッドの問題が発生する可能性はありますか?

プログラミングQA


次の実験コードを使用すると:

C#
var client = new HttpClient();

Task<HttpResponseMessage> response = client.GetAsync(url);
response.WaitAsync(TimeSpan.FromMilliseconds(500));

Console.WriteLine("Done async action. response.IsCompleted: {0}, and status code is {1}, task status: {2}", response.IsCompleted, response.Result.StatusCode, response.Status);

結果のログ行は私に与えます:

「ターミナル」
Done async action. response.IsCompleted: False, and status code is OK, task status: RanToCompletion

そのため、タスクのステータスが RanToCompletionIsCompleted プロパティは False.

私が試したこと:

シンプルに使うと Wait、したがって、タイムアウトがなければ、期待される結果が得られます IsCompleted に設定されています True:

C#
var client = new HttpClient();
Task<HttpResponseMessage> response = client.GetAsync(url);

response.Wait();

結果のログ行:

「ターミナル」
Done async action. response.IsCompleted: True, and status code is OK, task status: RanToCompletion

私は私が使用できることを知っています await 上のキーワード GetAsync メソッドですが、前述のように、これは実験的なコードです。

期待される結果が得られるもう 1 つのことは、セマフォを導入する場合です。

C#
Semaphore sem = new Semaphore(0, 1);

var client = new HttpClient();
Task<HttpResponseMessage> response = client.GetAsync(url);
response.GetAwaiter().OnCompleted(() => { sem.Release(); });

response.WaitAsync(TimeSpan.FromMilliseconds(500));
sem.WaitOne(1000);

ログ行を複製すると、2 番目のログ行が する 見せる True. このような:

「ターミナル」
Done async action. response.IsCompleted: False, and status code is OK, task status: RanToCompletion
Done async action. response.IsCompleted: True, and status code is OK, task status: RanToCompletion

これは単なる実験的なコードなので、まったく重要ではありません。 しかし、何が起こっているのか、何が間違っているのかを知っている人がいるのだろうかと思っていました.

解決策 1

ここにはいくつかの問題があります。

初期化中 HttpClient この方法は遅く、リソースをすぐに使い果たしてしまいます。

あなたは IHttpClentFactory. Factory はインスタンスを保持してリサイクルするため、実体化が迅速になり、リソースの使用量が少なくなります。 ここでもっと読むことができます: IHttpClientFactory を使用して回復力のある HTTP 要求を実装する | マイクロソフト ラーン[^]

IOC (制御の反転) コンテナーを介して DI (依存性注入) を使用せずにそれを行う方法を示します。

C#
private readonly IHttpClientFactory _httpClientFactory;

の設定 IHttpClientFactory:

C#
ServiceCollection builder = new ServiceCollection();
builder.AddHttpClient(HttpClientKey);
ServiceProvider serviceProvider = builder.BuildServiceProvider();

_httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();

これは、アプリケーション全体で共有するための静的クラス (シングルトン デザイン パターン) にある必要があります。

次に、 HttpClient 実例:

C#
private HttpClient HttpClientFactory(Uri? uri = null)
{
    HttpClient client = _httpClientFactory.CreateClient(HttpClientKey);

    if (uri is not null)
        client.BaseAddress = uri;

    return client;
}

次に使用します:

C#
public async Task MyMethod()
{
    using HttpClient client = HttpClientFactory();
    {
    var response = await client.GetAsync(uri).ConfigureAwait(false);
    // do stuff here...
    }
}

私がラップしたことに注意してください HttpClientUsing 声明。 これはそうです HttpClient 正しく廃棄されます。

.ConfigureAwait(false) オプションですが、 MyMethod 呼び出しスレッド (ほとんどの場合 UI スレッド) から離れたスレッドプールからスレッドにマーシャリングされ、アプリケーションをロックしません。

解決策 2

必要がある awaitresponse.WaitAsync(TimeSpan.FromMilliseconds(500)) 内のメソッド try ブロックして、メソッドがスローする可能性のある例外をキャッチできるようにします。 私の推測では、 response.IsCompleted は偽であるため、 WaitAsync タイムアウトしました。 しない限り、例外をキャッチしません。 await それ。 これらの行に沿って何かを試してください:

C#
private static async Task Main(string[] args)
   {
    using   var client = new HttpClient();
       string url = $"https://www.codeproject.com/";
      Task<HttpResponseMessage> response =client.GetAsync(url);
       try
       {
         await  response.WaitAsync(TimeSpan.FromMilliseconds(500));
       }
       catch (Exception ex)
       {
           Console.WriteLine(ex.Message);
       }

       Console.WriteLine("Done async action. response.IsCompleted: {0}, and status code is {1}, task status: {2}", response.IsCompleted, response.Result.StatusCode, response.Status);

コメント

タイトルとURLをコピーしました