LoginSignup
4
0

More than 3 years have passed since last update.

「非同期メソッドが末尾に1個だけならそのTaskを返すと速い」とusingが合わさって死んだ回

Last updated at Posted at 2019-05-20

何をして死んだか

非同期メソッドの最適化として、こんなやつがある。

SlowFastTask.cs
async Task SlowTask()
{
    //この辺でもなんかやる

    //最後に1個だけ非同期メソッドを呼ぶ
    await DoSomethingAsync();
}

Task FastTask()
{
    //この辺でもなんかやる

    //直接Taskを返すとちょっと速い
    return DoSomethingAsync();
}

どうせ呼び出し側でawaitなどをするし、中のTaskを直接返せば二重awaitが解消されるのでちょっと速くなる、というやつ。

しかし、うっかりこういうことをやると死んでしまう。

SinuTask.cs
Task SinuTask()
{
    using (var thing = new Something())
    {
        //この辺でもなんかやる

        return thing.DoAsync();
    }
}

当たり前といえばそれまでだが、一応追ってみよう。

こうして死んでいったのだ

まず、usingの正体は try → finally内でDispose() だ。つまりこうなる。

SinuTaskConverted.cs
Task SinuTaskConverted()
{
    var thing = new Something();
    try
    {
       //この辺でもなんかやる

        return thing.DoAsync();
    }
    finally
    {
        thing.Dispose();
    }
}

そして概ねこんな風に実行される。

SinuTaskConverted2.cs
Task SinuTaskConverted2()
{
    var thing = new Something();
    //この辺でもなんかやる

    var ret = thing.DoAsync();

    //finallyの中身はreturnの前に実行される
    thing.Dispose();

    //ObjectDisposedExceptionで死ぬ
    //死ぬタイミングは thing.DoAsync() の中身次第
    return ret;
}

\(^o^)/オワタ

生きる方法

というわけで面倒でもawaitしよう。

IkiruTask.cs
async Task IkiruTask()
{
    using (var thing = new Something())
    {
        //この辺でもなんかやる

        //これならDoAsync()が終わった後にDispose()される
        await thing.DoAsync();
    }
}
4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0