4

私は自分のプログラムがこのコールスタック/ワークフローに従うことを望んでいます:

  1. dispatch()
  2. authorize()
  3. httpPost()

私の考えではhttpPost()、他の 2 つのメソッドは非同期のままですが、それは非同期になります。しかし、なぜか2と3をasyncにしないと動かない。もしかしたら、まだ誤解があるかもしれません。

私の理解では、次のいずれかを実行できます。

  1. async メソッドを呼び出すときにキーワードを使用するawait(これにより、メソッドが一時停止され、async メソッドの完了後に続行されます)、または
  2. awaitキーワードを省略し、代わりTask.Resultに非同期メソッドの戻り値を呼び出します。これは、結果が利用可能になるまでブロックされます。

これが実際の例です:

private int dispatch(string options)
{
    int res = authorize(options).Result;
    return res;
}

static async private Task<int> authorize(string options)
{
    string values = getValuesFromOptions(options);
    KeyValuePair<int, string> response = await httpPost(url, values);
    return 0;
}

public static async Task<KeyValuePair<int, string>> httpPost(string url, List<KeyValuePair<string, string>> parameters)
{
    var httpClient = new HttpClient(new HttpClientHandler());
    HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters));
    int code = (int)response.StatusCode;
    response.EnsureSuccessStatusCode();

    string responseString = await response.Content.ReadAsStringAsync();
    return new KeyValuePair<int, string>(code, responseString);
}

動作しない例を次に示します。

private int dispatch(string options)
{
    int res = authorize(options).Result;
    return res;
}

static private int authorize(string options)
{
    string values = getValuesFromOptions(options);
    Task<KeyValuePair<int, string>> response = httpPost(url, values);
    doSomethingWith(response.Result);    // execution will hang here forever
    return 0;
}

public static async Task<KeyValuePair<int, string>> httpPost(string url, List<KeyValuePair<string, string>> parameters)
{
    var httpClient = new HttpClient(new HttpClientHandler());
    HttpResponseMessage response = await httpClient.PostAsync(url, new FormUrlEncodedContent(parameters));
    int code = (int)response.StatusCode;
    response.EnsureSuccessStatusCode();

    string responseString = await response.Content.ReadAsStringAsync();
    return new KeyValuePair<int, string>(code, responseString);
}

awaitまた、3 つのメソッドすべてを非非同期にして、 shttpPostをsに置き換えようとし.Resultましたが、回線上で永久にハングします。HttpResponseMessage response = httpClient.PostAsync(url, new FormUrlEncodedContent(parameters)).Result;

誰かが私を啓発し、私の間違いが何であるかを説明できますか?

4

1 に答える 1

12

があり、そのコンテキストで継続を実行できるように、SynchronizationContextそのコンテキストがキャプチャされます。await

非同期タスクを開始し、後でメイン コンテキストで実行する継続をスケジュールします。

次に、非同期操作が完了する前に、非同期操作でブロッキング待機を行うメイン コンテキストのコードがあります。コンテキストが継続の待機でビジーであるため、継続の実行をスケジュールできません。古典的なデッドロック。

これが、最初の例で行ったように、「完全に非同期にする」ことが重要な理由です。

2 番目の例のデッドロックを回避するためのハックがいくつかありますが、それでも実行すべきことではありません。非同期に移行する最大のポイントは、スレッドのブロックを回避することです。とにかくタスクでブロッキング待機を行うだけでは、非同期にする目的が無効になります。選択肢がない場合を除き、すべてを非同期にするか、何も非同期にしないでください。

于 2013-09-05T15:05:35.503 に答える