非同期処理のいろいろ
初心者プログラマーにとって、
非同期処理はとてもハードルが高い。
方法が一つだけならまだしも、
いろいろな手法があって自分の中でなかなか整理できない。
ということで実際にコードを書いてまとめてみた。
参考にした本は、これ。
gihyo.jp
参考書の中ではWindowsフォーム用のコードが書かれていたので、
WPF用に少し修正した。
まず、Threadクラスを使用した非同期処理。
private string _labelText; public string LabelText { get { return _labelText; } set { Set(ref _labelText, value, "LabelText"); } } private RelayCommand _buttonClickCommand; public RelayCommand ButtonClickCommand { get { return _buttonClickCommand ?? (_buttonClickCommand = new RelayCommand(OnButtonClick)); } } public void OnButtonClick() { LabelText = "開始"; //Threadクラスを使用した非同期処理 var th = new Thread(DoLongSomething); th.Start(); } private void DoLongSomething() { Thread.Sleep(5000); LabelText = "終了"; }
簡単に説明をすると、View側にボタンとラベルを配置して、
ボタン開始時にラベルのテキストを変更し、
5秒待機した後、再度ラベルのテキストを変更するというもの。
Viewには別のコントロールも配置していて、
5秒待機している間、UIがフリーズすることなくコントロールを操作できるかを確認している。
この全体の動作はこのあとに登場するコードでも同様。
続いて、Taskクラスを使用した非同期処理。
public void OnButtonClick() { LabelText = "開始"; //Taskクラスを使用した非同期処理 Task.Run(() => DoLongSomething()); } private void DoLongSomething() { Thread.Sleep(5000); LabelText = "終了"; }
次に、await/asyncを使用し、戻り値がないメソッドを呼ぶ。
ポイントは、OnButtonClickメソッドにsysnc修飾子をつけること。
asyncをつけることでawaitが使用可能になり、
awaitをつけたTaskはそのタスクが完了するまで待機する。
public async void OnButtonClick() { LabelText = "開始"; //awaitを使用した非同期処理 await Task.Run(() => DoLongSomething()); //書き方違うバージョン await Task.Run(() => { DoLongSomething(); }); //分割して書くと以下 Task task = Task.Run(() => DoLongSomething()); await task; LabelText = "終了"; } private void DoLongSomething() { Thread.Sleep(5000); }
await/asyncを使用し、戻り値があるメソッドを呼ぶ。
public async void OnButtonClick() { LabelText = "開始"; //戻り値のあるメソッドを非同期処理 var elapsed = await Task.Run(() => DoLongSomething()); LabelText = $"{elapsed}ミリ秒"; } /// <summary> /// 戻り値のある同期メソッド /// </summary> /// <returns></returns> private long DoLongSomething() { var sw = Stopwatch.StartNew(); Thread.Sleep(5000); sw.Stop(); return sw.ElapsedMilliseconds; } 今度は、メソッド自体を非同期メソッドに。戻り値がないパターン。 ポイントは、DoLongSomethingをDoLongSomethingAsyncにして、 async修飾子をつけ、戻りの型をTaskにする。 >|cs| public async void OnButtonClick() { LabelText = "開始"; //戻り値の無い非同期処理メソッドを呼ぶ await DoLongSomethingAsync(); LabelText = "終了"; } /// <summary> /// 戻り値の無い非同期メソッド /// </summary> /// <returns></returns> private async Task DoLongSomethingAsync() { await Task.Run(() => Thread.Sleep(5000)); }
戻り値がある非同期メソッド。
DoLongSomethingAsyncの戻りの型はTask
public async void OnButtonClick() { LabelText = "開始"; //戻り値のある非同期処理メソッドを呼ぶ var elapsed = await DoLongSomethingAsync(); LabelText = $"{elapsed}ミリ秒"; } /// <summary> /// 戻り値のある非同期メソッド /// </summary> /// <returns></returns> private async Task<long> DoLongSomethingAsync() { var sw = Stopwatch.StartNew(); await Task.Run(() => Thread.Sleep(5000)); sw.Stop(); return sw.ElapsedMilliseconds; }