C Sharpの基礎 - マルチスレッド
ナビゲーションに移動
検索に移動
概要
asyncとawaitの動作
Taskは、一連の処理をひとまとまりにした単位である。
Taskを呼び出した際はマルチスレッドで実行されるようになっており、連続で記述する場合は、記述した個数だけマルチスレッドで並列処理される。
public async Task MethodAsync()
{
var task1 = Task.Run(() => {
// 何か処理1
});
var task2 = Task.Run(() => {
// 何か処理2
});
var taskAll = Task.WhenAll(task1, task2).ContinueWith(() => {
// 何か処理3
});
return await taskAll.ConfigureAwait(false);
}
Task.WhenAllとContinueWith
TaskはContinueWith関数を利用して、その後の処理を連続して実行できる。
2つ以上のスレッドを実行して全スレッドの処理が終了した後、続けてスレッドの処理を書く場合は、
ContinueWithまたはawaitを使用してスレッドの終了を待つ。
// パターン1
var task1 = Task.Run(() =>
{
// 何か処理
});
var task2 = Task.Run(() =>
{
// 何か処理
});
var taskAll = Task.WhenAll(task1, task2).ContinueWith(task3);
// パターン2
var task1 = Task.Run(() =>
{
// 何か処理
});
var task2 = Task.Run(() =>
{
// 何か処理
});
await task1.ConfigureAwait(false);
await task2.ConfigureAwait(false);
// task3を普通に記述
状況にもよるが、処理が膨大になると可読性が落ちるので、
先に関数で纏めて処理を記述して関数を呼ぶだけでタスク間の関係性が見通せるパターン1の方が良い。
ConfigureAwait(false)
taskのWaitメソッドやResultは、デフォルトで元のスレッドへ戻ろうとする。
そのため、下記のように記述すると簡単にデッドロックが発生する。
public void callMethod()
{
methodAsync().Wait();
}
public Task methodAsync()
{
return await Task.Delay(1000);
}
上記の場合、methodAsync().Wait()を呼んだ時点で親スレッドがスリープする。
次に、methodAsync()が終了後、親スレッドに戻ろうとするが、既に親スレッドがスリープしているため処理を続けることができない。
その結果、親スレッドは子スレッドを待ち続け、子スレッドは親スレッドに戻ろうとして、デッドロックが発生する。
対策として、以下のように記述するとデッドロックが回避できる。(戻るスレッドはどこでもよい場合)
public void callMethod()
{
methodAsync().Wait();
}
public Task waitAsync()
{
return await Task.Delay(1000).ConfigureAwait(false);
}