目的
現場でC#を使っています。非同期処理について自分なりに学習した記録です。誤りがあれば指摘をお願いします。
同期処理
一つずつ順番に処理を実行する方法。前の処理が終わるまで次の処理は実行されない。
処理の流れ
①button1をクリック
②検索処理(GetData()メソッド実行)
③表示
途中でcheckBoxにチェックを入れることはできない。
Form1.cs
private void button1_Click(object sender, EventArgs e)
{
dataGridView1.DataSource = GetData();
}
private List<DTO> GetData()
{
var result = new List<DTO>();
for (int i = 0; i <5; i++)
{
Thread.Sleep(1000);
result.Add(new DTO(i.ToString(),"Name" + i));
}
return result;
}
非同期処理
前の処理が終わる前に、次の処理も実行する方法。
処理の流れは同期処理と同じですが、大きな違いは途中でもチェックボックスにチェックを入れることができる。
メインスレッド(UIスレッド)に加えて、非同期処理を行うワーカースレッドが生成される。
主な方法としては、三つある。
①Threadクラスを用いた方法
Form1.cs
private void button2_Click(object sender, EventArgs e)
{
var t = new Thread(GetData); // Threadクラスの引数に非同期で処理したい関数(GetData)を指定。
t.Start(); //非同期処理開始
}
private void GetData()
{
var dtos = new List<DTO>();
for (int i = 0; i < 5; i++)
{
Thread.Sleep(1000);
dtos.Add(new DTO(i.ToString(), "Name" + i));
}
//dataGridView1.DataSource = dtos; //エラーが発生する
//UIスレッド外のスレッドから直接UIコントロールを操作することは避ける
this.Invoke((Action)delegate ()
{
//UIスレッド上で実行するコードを記述する
dataGridView1.DataSource = dtos;
});
}
②Taskを用いた方法
①と比較すると複雑そうにみえますが、文構造としてはシンプルです。
Task.Run(非同期メソッド).ContinueWith(action, context)
action→非同期メソッド完了後に行う処理
context→メインスレッドの環境(ざっくりいうと)
Task.Run→非同期処理を開始
ContinueWith()メソッドで非同期処理後に行う処理を指定
Form1.cs
{
public partial class Form1 : Form
{
private void button2_Click(object sender, EventArgs e)
{
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(
() => GetData()
).ContinueWith
(
dtos =>
{
dataGridView1.DataSource = dtos.Result;
},
context
);
}
}
}
③awaitを用いた方法
最もコード量が少なく、使用頻度も高い方法。
async→非同期メソッドになる
await→非同期処理に対する実行制御を行う。コード例でいうと、非同期処理(GetData()メソッド)の完了を待ってから、UIスレッド上で処理(dataGridView1.DataSource = dtos;)が行われる。
Form1.cs
private async void button3_Click(object sender, EventArgs e)
{
var dtos = await Task.Run(() => GetData());
dataGridView1.DataSource = dtos;
}