何をして死んだか
非同期メソッドの最適化として、こんなやつがある。
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();
}
}