11

async / await以下に出くわしたとき、私は遊んでいました:

class C
{
    private static string str;

    private static async Task<int> FooAsync()
    {
        str += "2";
        await Task.Delay(100);
        str += "4";
        return 5;
    }

    private static void Main(string[] args)
    {
        str = "1";
        var t = FooAsync();
        str += "3";

        str += t.Result; // Line X

        Console.WriteLine(str);
    }
}

結果は「12345」になると思っていたのですが、「1235」でした。なぜか「4」が食べ尽くされました。

行Xを次のように分割すると:

int i = t.Result;
str += i;

次に、予想される「12345」の結果が得られます。

なぜそうなのですか?(VS2012を使用)

4

3 に答える 3

10

なぜそうなのですか?(VS2012を使用)

これをコンソール アプリケーションで実行しているため、現在の同期コンテキストはありません。

そのため、FooAsync()メソッドの の後の部分はawait別のスレッドで実行されます。あなたがそうするとき、str += t.Resultあなたは事実上、+= 4呼び出しと+= t.Result. これはstring +=、アトミック操作ではないためです。

Windows フォームまたは WPF アプリケーション内で同じコードを実行する場合、同期コンテキストがキャプチャされ、+= "4".

于 2013-05-24T20:49:53.273 に答える
7

フォームの C# ステートメントは、コンパイル時x += y;に展開されます。x = x + y;

str += t.Result;となりますstr = str + t.Result;、どこstrをお読みになるにお読みくださいt.Result。この時点で、strです"123"。in の継続がFooAsync実行されると、変更strして を返します5。今もそうstrです"1234"。しかし、run の継続(つまり) がvalueに代入されるstrに読み取られたその値が連結されます。FooAsync"123"5str"1235"

2 つのステートメントに分割するとint i = t.Result; str += i;、この動作は発生しません。

于 2013-05-24T20:52:46.290 に答える
6

これは競合状態です。2 つの実行スレッド間で共有変数へのアクセスを適切に同期していません。

あなたのコードはおそらく次のようなことをしています:

  • 文字列を「1」に設定します
  • FooAsync を呼び出す
  • 追加 2
  • await が呼び出されると、main メソッドの実行が継続され、FooAsync のコールバックがスレッド プールで実行されます。ここから先は未定です。
  • メインスレッドは文字列に 3 を追加します

次に、興味深い行に到達します。

str += t.Result;

ここでは、いくつかの小さな操作に分割されています。最初に の現在の値を取得しますstr。この時点では、非同期メソッドは (ほぼ確実に) まだ終了していないため、"123". 次に、タスクが完了するのを待ち (Resultブロッキング待機を強制するため)、タスクの結果を、この場合5は文字列の末尾に追加します。

非同期コールバックは、メイン スレッドが の現在の値を既に取得した後に取得して再設定str 、メイン スレッドがすぐに上書きするため、読み取られることなく上書きされます。strstr

于 2013-05-24T20:53:36.897 に答える