#はじめに
MSDNのAsyncのページを読んでいて、「戻り値がTask?voidでもいい?ワカ分からん」となったので実際に動かしてどのような挙動になるかを確認しました。
MSDNによると非同期メソッドにするには
- メソッドのシグネチャにasyncをつける。
- できればメソッド名の後ろに"Async"をつける。
- 戻り値の型を、void、Task、Task<TResult>のどれかにする
とすれば良いみたいです。
今回は戻り値の型の違いによるそれぞれの動作を見ていきたいと思います。検証したのは戻り値がvoidの場合、Taskの場合の2つです。Task<TResult>は戻り値を返す以外はTaskとほとんど同じ動作をするため記載を省略しています。
今回のソースコードはVisual Studio 2015で実行しています。
参考:Async および Await を使用した非同期プログラミング (C# および Visual Basic)
https://msdn.microsoft.com/ja-jp/library/hh191443(v=vs.110).aspx
#非同期メソッドの戻り値がvoidのとき
以下のコードで動作を確認しました。
非同期処理の戻り値による動作の違いを見たかったため、Thread.Sleep()とConsole.WriteLine()を入れ、処理の順序が分かるようにしています。
また、例外処理を行なうかを確認したかったためAsyncVメソッドで例外を投げています。
本当は非同期メソッドが入れ子になったときの状態を確認するためにbutton1_Clickでawaitしたかったのですが、Visual Studio 2015では戻り値voidの非同期メソッドはawaitできないみたいです(Visual Studio 2013では書けた気がする)。
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("ボタン開始");
button1.Enabled = false;
try
{
// 本当は await AsyncV();としたかった
AsyncV();
}
catch
{
Console.WriteLine("例外発生");
}
Console.WriteLine("ボタン終了");
button1.Enabled = true;
}
private async void AsyncV()
{
Console.WriteLine("非同期処理開始");
Thread.Sleep(1000);
await Task.Run(SomeMethod);
Console.WriteLine("非同期処理終了");
throw new Exception();
}
private async Task SomeMethod()
{
await Task.Run(() => { Thread.Sleep(5000); });
}
##出力結果
ボタン開始
非同期処理開始
ボタン終了
非同期処理終了
- 1秒間ボタンが使用不可になる
- さらにハンドルされていない例外が発生する
#非同期メソッドの戻り値がTaskのとき
AsyncVメソッドの戻り値をTaskに変更したのが下記のコードです。
今度は呼び出しの際にawaitを行えるようになっています。
private async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("ボタン開始");
button1.Enabled = false;
try
{
await AsyncT();
}
catch
{
Console.WriteLine("例外発生");
}
Console.WriteLine("ボタン終了");
button1.Enabled = true;
}
private async Task AsyncT()
{
Console.WriteLine("非同期処理開始");
Thread.Sleep(1000);
await Task.Run(SomeMethod);
Console.WriteLine("非同期処理終了");
throw new Exception();
}
private async Task SomeMethod()
{
await Task.Run(() => { Thread.Sleep(5000); });
}
##出力結果
ボタン開始
非同期処理開始
非同期処理終了
例外発生
ボタン終了
- 6秒間ボタンが使用不可になる
#まとめ
戻り値voidの場合、
- 呼び出し元でawaitが行えない
- 非同期処理が終了する前に、呼び出し元のメソッドが実行される
- 呼び出し元で例外をキャッチできない
戻り値Taskの場合、
- 呼び出し元でawaitを行える
- 非同期処理が終了してから、呼び出し元のメソッドが実行される
- 呼び出し元で例外をキャッチできる