1. C#のasync awaitについて
・非同期処理がよくわからない(ググってもよくわからん)と言われたので...
・確かにググって最初の方に出てくるwebページはいろいろ説明が多すぎて(丁寧過ぎて)初心者にはよくわかないかもしれない。
・このため以下のようにやれば簡単にできる。できるようになったらググって色々調べてね。をモットーに作成。
・ウインドウフォームアプリで時間のかかる処理を行うときはアプリが止まらないように非同期で行いたい。
・C#では非同期で処理させる時は「async await」が一番簡単。
・関数名に「async」をつけて、重い処理をさせるところに「await」をつければ非同期処理ができる!
2. 今回作るもの
・クリックしたら5秒後にテキストボックスに「5秒後」と表示するだけ
・テキストボックスは一個。ボタンは二個(同期と非同期)
3. 画面について
・ウインドウフォームアプリを作成し、テキストボックスを一個、ボタンを二個貼り付ける。
・ボタンは「同期」(Sync)と「非同期」(Async)へ変更。テキストボックスはtextBox1のまま。
4. 同期のイベントハンドラ
・「同期」へイベントハンドラを追加して、以下に変更。
・当たり前だけど、5秒間アプリは止まって、5秒後にtextBox1に「5秒後」と表示。
private void Sync_Click(object sender, EventArgs e)
{
Thread.Sleep(5000);
textBox1.Text = "5秒後";
}
5. 非同期のイベントハンドラ
・「非同期」へイベントハンドラを追加して、関数名に「async」を追加。
・SleepをTask.Run()から呼び出す。そのときに「await」をつける。すると...
private async void Async_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
Thread.Sleep(5000);
});
textBox1.Text = "5秒後";
}
・5秒間止まらずに、5秒後に「5秒後」と出てくるようになりました!
・Task.Runに「textBox1.Text = "5秒後";」まで含めていないのは意味があります。
・「async」を付けて関数を宣言すると、「await」を付けた部分は別スレッドで実行され、それが終わるとUIスレッドに戻って処理が実行されるようになります。なので、UI更新までTask.Runに入れるとエラーを起こして怒られるので入れていません。
・これで、非同期処理とUIの更新ができるようになりました。簡単でしょう?
・ある程度理解できるようになったら、ラムダ式、Task、Thread等ちゃんと調べるようにしてください。
・さて、次は課題です。
6. 課題
・Async_Clickをクリックすると、1秒ごとに1~100までカウントアップする処理へ変更してください。
・コメントには課題の回答を書かないでね。うちの新入社員用だから!
7. 別スレッドでUI更新をしたい場合
・Invoke(呼び出す)からUI関連のメンバを呼び出せばOK。
・「非同期2」(Async2)ボタンを追加して、同じようにイベントハンドラを追加。そして以下。
private async void Async2_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
Thread.Sleep(5000);
Invoke((MethodInvoker)(() =>
{
textBox1.Text = "5秒後";
}));
});
}
・値を更新できると言うことは、値を取得できると言うこと。なので取得もInvokeからできる。
・もし、2個以上のスレッドから同時にアクセス(特に書き込まれる)する可能性があるときは「semaphore」もしくは「SemaphoreSlim」を使用して同時に読み書きや同時に書き込みしないように制御してください。「lock」でも良いけど、「lock」だとawaitが使えない。
8. 別スレッドで動く関数を呼び出す。
・「async Task<>」型で定義した関数を「await」をつけて呼び出せば、非同期で関数を呼び出せる。
・「await」をつけて呼び出すと「Task<>」の<>の中の型でかえってくる。
・「非同期3」のボタンと「checkBox1」を追加。すると以下の画像
・「非同期3」をクリックすると5秒後にチェックボックスの結果を表示するようにしてみましょう。
・private async Task IsCheck1()関数を定義して、それをイベントハンドラから呼び出します。
private async Task<bool> IsCheck1()
{
bool check1 = false;
await Task.Run(() =>
{
Thread.Sleep(5000);
Invoke((MethodInvoker)(() =>
{
if(checkBox1.Checked == true)
check1= true;
else
check1= false;
}));
});
return check1;
}
private async void Async3_Click(object sender, EventArgs e)
{
bool check1 = await IsCheck1();
if(check1== true)
textBox1.Text = "チェック有";
else
textBox1.Text = "チェック無";
}
・これで非同期でUIの状態取得、UIの更新が可能に。
・後はさっきも書いたけど、競合するスレッドのアクセス制御(同期)ができるようになれば完璧。
・また、例えば戻り値が必要の無い場合はどうなるか、スレッドを投げっぱなしでよい場合はどうなるのか等、色々考えて実装してみてください。
・というわけで非同期なんて簡単簡単。