【C#】Taskのtry-catchメモ
class Program { public static int Main(string[] args) { try { var task = Task<Hoge>.Factory.StartNew(() => { Thread.Sleep(10000); throw new ApplicationException("piyo"); return new Hoge { Fuga = "hogepiyo" }; }); if (task.Result.Fuga == "hogepiyo") { return 0; } else { return 1; } } catch (Exception) { return -1; } } } public class Hoge { public string Fuga { get; set; } }
実行するとcatch(Exception)で捕まえてくれそうだけれど、実際にはthrow new ApplicationException(“piyo”)を捕まえられずにエラーが起きる。 なので、StartNew()内を書き換えてやる。
var task = Task<Hoge>.Factory.StartNew(() => { try { Thread.Sleep(10000); throw new ApplicationException("piyo"); return new Hoge { Fuga = "hogepiyo" }; } catch (ApplicationException) { return new Hoge { Fuga = "piyo" }; } });
これだとHoge.Fugaが”piyo”なので、無事に1が返ってくる。 Waitを使ってタイムアウト設定をしたいだとか、エラーは伝播させて親スレッドで拾いたいとかログをとりたいとかになるともうちょっと面倒になる。 (ログはStartNew内でもとれるけど。)
public static int Main(string[] args) { try { var task = Task<Hoge>.Factory.StartNew(() => { Thread.Sleep(10000); throw new ApplicationException("piyo"); return new Hoge { Fuga = "hogepiyo" }; }); if (task.Wait(TimeSpan.FromSeconds(15))) { //Task.isFaultedがtrueの場合は例外発生によって終了 if (!task.IsFaulted) { return task.Result.Fuga == "hogepiyo" ? 0 : 1; } else { //Task.Exceptionの中身はAggregateExceptionなのでInnerExceptionで拾ってやること var taskException = task.Exception; Console.WriteLine(taskException.InnerException.Message ?? taskException.Message); return -1; } } else { return 2; } } catch (Exception) { return -1; } }
当然この判定はContinueWithでも出来る。
var task = Task<Hoge>.Factory.StartNew(() => { try { Thread.Sleep(10000); throw new ApplicationException("piyo"); return new Hoge { Fuga = "hogepiyo" }; } catch (ApplicationException) { return new Hoge { Fuga = "piyo" }; } }).ContinueWith(t => { if (!t.IsFaulted) { return t.Result.Fuga == "hogepiyo" ? 0 : 1; } else { var taskException = t.Exception; Console.WriteLine(taskException.InnerException.Message ?? taskException.Message); return -1; } });
ちなみに、そもそもシングルコアだと一番上のcatch(Exception)で捕まえられる。テスト時は要注意。