TAPベースに非同期処理に関連して、

ネットを調べると、「同期と非同期の混在によるデッドロック」みたいな記事が多い。

これ、よく調べてみると、.NET FRAMEWORKの話だったりして、

.NET(core以降)のASPでは、どうやら事情が違うようです。

(※GUIについては、ここでは触れません)

CUI(コンソール)アプリケーションについては、もともとSyncronizedContextがないから、

デッドロックしないよ、みたいな話はあるのですが、

ASP.NET Core以降は、SyncronizedContextがなくなったため、よくあるデッドロックというのは起きません。

(参考)

https://blog.stephencleary.com/2017/03/aspnetcore-synchronization-context.html

https://qiita.com/jun1s/items/c178d37a2cbcd8594461

というわけで、デッドロックしそうなコードをかき集めて検証してみました。

下記コードの検証環境は以下の通りです。

$ dotnet --version
8.0.204

$ cat /etc/os-release
NAME="Pop!_OS"
VERSION="22.04 LTS"

(パターン1)

app.MapGet("/test1", async () =>
{
    Console.WriteLine("test 1-------------------------");
    Console.WriteLine($"before - {Thread.CurrentThread.ManagedThreadId}");  // 1: スレッドIDは15
    var ret = MyClass.Get().Result;
    Console.WriteLine($"after - {Thread.CurrentThread.ManagedThreadId}");  // 4: スレッドIDは15
    return ret;
});

class MyClass
{
    public static async Task<string> Get()
    {
        Console.WriteLine($"before in - {Thread.CurrentThread.ManagedThreadId}"); // 2: スレッドIDは15
        var ret = await Task.Run(() => "ok");
        Console.WriteLine($"after in - {Thread.CurrentThread.ManagedThreadId}");  // 3: スレッドIDは14 ★ここでスレッドが変わっている
        return ret;
    }
}

(パターン2)

app.MapGet("/test4", async () =>
{
    Console.WriteLine("test 4-------------------------");
    Console.WriteLine($"before - {Thread.CurrentThread.ManagedThreadId}");  // 1: スレッドIDは9
    var result = MyClass.GetFromHttp().Result;
    Console.WriteLine($"after - {Thread.CurrentThread.ManagedThreadId}");   // 4: スレッドIDは9
    return result;
});

class MyClass
{
    public static async Task<string> GetFromHttp()
    {
        var client = new HttpClient();
        Console.WriteLine($"before in - {Thread.CurrentThread.ManagedThreadId}");  // 2: スレッドIDは9
        var response = await client.GetAsync("http://localhost:8080");
        Console.WriteLine($"after in - {Thread.CurrentThread.ManagedThreadId}");    // 3: スレッドIDは7 ★ここでスレッドが変わっている
        return response.Content.ReadAsStringAsync().Result;
    }
}

というわけで、色々勘違いしそうなんですが、

ASP.NET Core以降では、「同期と非同期の混在」によるデッドロックは起きないようです。

つまり、「ConfigureAwait(false)」がなくても良いのです。

※すいません、間違ってたら教えて下さい。

とはいえ、ライブラリアプリケーションの場合は、

どの実行基盤で使われるかわからないので、「ConfigureAwait(false)」はつけておいたほうが良いかと思います。

C#のasync/awaitは沼だなといつも思う。

わからないことだらけです。

ちょっとしか見てないんですが、Restsharp なんかでは内部でSyncronizedContextを持っているようで、

どうしてこうなっているのかは謎が深まるばかりです。

いつか、スッキリ整理してまとめたいと思うこの頃。

できれば、IO待ちに関しては、「AWAIT」ではなく「NOWAIT」にして欲しかったりする。

つまり、非同期をデフォルトってことですかね。(新しいMSの言語まで待ちかな?)

以上

コメントがあればどうぞ


CAPTCHA Image
Reload Image