つぎのC#はasync/awaitってことでいろいろ見てたんですが、挙動がいまいちつかめなかったので残しときます。
- WinRTアプリケーションでSleepしたい - かずきのBlog@hatena
- A simple example of async and await in C# 5 « Tim Anderson's ITWriting
いろいろ教えてもらって、とりあえずは理解しました
最初
とりあえず、同期で待たされるメソッドを作りました。
ハズが待たされませんw
private void button1_Click(object sender, RoutedEventArgs e) { this.label1.Text = ""; var stopwatch = Stopwatch.StartNew(); var stopwatch2 = Stopwatch.StartNew(); this.button1.IsEnabled = false; //prevent re-entry var someTask = Task<int>.Factory.StartNew(() => slowFunc(1, 2)); someTask.ContinueWith(x => { // 5秒後 this.label1.Text = "Result: " + someTask.Result.ToString(); this.button1.IsEnabled = true; stopwatch2.Stop(); this.label3.Text = string.Format("{0}ms, {1}ms", stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds); }, TaskScheduler.FromCurrentSynchronizationContext()); stopwatch.Stop(); // 5秒後 this.label2.Text = string.Format("{0}ms, {1}ms", stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds); } // 5秒待つ private int slowFunc(int a, int b) { // ここでwaitされるハズ? Task.Delay(TimeSpan.FromSeconds(5)); return a + b; }
次に
非同期で実行されるように改造しました。
意図したとおりに実行されました
private void button1_Click(object sender, RoutedEventArgs e) { this.label1.Text = ""; var stopwatch = Stopwatch.StartNew(); var stopwatch2 = Stopwatch.StartNew(); this.button1.IsEnabled = false; //prevent re-entry var someTask = Task<Task<int>>.Factory.StartNew(() => slowFunc(1, 2)); someTask.ContinueWith(x => { // slowFuncが終わってからなので、5秒後に実行される this.label1.Text = "Result: " + someTask.Result.Result.ToString(); this.button1.IsEnabled = true; stopwatch2.Stop(); this.label3.Text = string.Format("{0}ms, {1}ms", stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds); }, // 非同期処理なので、すぐに処理される TaskScheduler.FromCurrentSynchronizationContext()); stopwatch.Stop(); this.label2.Text = string.Format("{0}ms, {1}ms", stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds); } private async Task<int> slowFunc(int a, int b) { // 5秒待つ(非同期) await Task.Delay(TimeSpan.FromSeconds(5)); return a + b; }
最後に
非同期処理のawaitを外して、同期にしたつもりです。
なってなさそうです。
private void button1_Click(object sender, RoutedEventArgs e) { this.label1.Text = ""; var stopwatch = Stopwatch.StartNew(); var stopwatch2 = Stopwatch.StartNew(); this.button1.IsEnabled = false; //prevent re-entry var someTask = Task<Task<int>>.Factory.StartNew(() => slowFunc(1, 2)); someTask.ContinueWith(x => { // slowFuncが終わってからなので、5秒後に実行されるハズ this.label1.Text = "Result: " + someTask.Result.Result.ToString(); this.button1.IsEnabled = true; stopwatch2.Stop(); this.label3.Text = string.Format("{0}ms, {1}ms", stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds); }, // 同期してるはずなので、5秒後に実行されるハズ TaskScheduler.FromCurrentSynchronizationContext()); stopwatch.Stop(); this.label2.Text = string.Format("{0}ms, {1}ms", stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds); } private async Task<int> slowFunc(int a, int b) { // 5秒待つ(同期) Task.Delay(TimeSpan.FromSeconds(5)); return a + b; }
ということで
awaitableのメソッドは、awaitしなさいってことなのかもしれませんが、同期実行されますよといわれるだけで、エラーにもならないのでよくわかりません。
asyncつけてるためとか?
いろいろ教えてもらいました
- awaitをつけると、「以降の処理(a + b)」が非同期で実行される(呼び出し元に返る)
- Task.Delay自体も非同期で実行され、awaitをつけるとDelayした後に、「以降の処理」が実行される
- Task.Delayにawaitをつけない場合は、Delayが非同期に実行されるので、「以降の処理」はDelayと並列で実行される(ので、Delayされない)
- awaitをつけないTask.Delayは、戻り値のTask.GetAwaiter().OnCompletedにDelay後のコールバック先を登録してTask.Wait()すると同じようになる
- awaitをつけるTask.Delayは、Task.GetAwaiter().OnCompletedとTask.Wait()をやってるのと同じ扱い(もし、Task.GetAwaiter().IsCompletedだったら、そのまま続ける)
private void button1_Click(object sender, RoutedEventArgs e) { this.label1.Text = ""; var stopwatch = Stopwatch.StartNew(); var stopwatch2 = Stopwatch.StartNew(); this.button1.IsEnabled = false; //prevent re-entry var someTask = Task<Task<int>>.Factory.StartNew(() => slowFunc(1, 2)); someTask.ContinueWith(x => { this.label1.Text = "Result: " + someTask.Result.Result.ToString(); this.button1.IsEnabled = true; stopwatch2.Stop(); this.label3.Text = string.Format("{0}ms, {1}ms", stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds); }, TaskScheduler.FromCurrentSynchronizationContext()); stopwatch.Stop(); this.label2.Text = string.Format("{0}ms, {1}ms", stopwatch.ElapsedMilliseconds, stopwatch2.ElapsedMilliseconds); } private async Task<int> slowFunc(int a, int b) { var t = Task.Delay(TimeSpan.FromSeconds(5)); int c = 0; t.GetAwaiter().OnCompleted(new Action(() => { c = a + b; })); t.Wait(); return c; }