前提知識
・スレッドには2種類がある。「UIスレッド」と「ワーカースレッド」
・「UIスレッド」は、ボタン押下等で起動するスレッド
・「ワーカースレッド」は、ThreadクラスのStartメソッド等で起動するスレッド
Threadクラスについて
1.非同期処理を実現するためのクラス
2.非同期処理を実現するための複数ある内の一つ。複数ある内で一番古い実現方法
・Threadクラス(.NET Framework1.1以降)
・ThreadPoolクラス(.NET Framework1.1以降)
・BackGroundWorkerクラス(.NET Framework2.0以降)
・Taskクラス(.NET Framework4.0以降)
・Taskクラスとasync/await(.NET Framework4.5以降)
3.ThreadクラスのStartメソッドの引数に指定するのは、スレッドとして起動したいメソッド。
このメソッドは戻り値無し(void)のみ許されている
4.以下のデメリットがあるため、可能ならばThreadクラスは使用しない
┗通常、ThreadクラスのStartメソッドで起動するスレッド(ワーカースレッド)からは、
UIのコントロールにアクセス不可
┗「デリゲート」を用いれば、アクセス可
┗「デリゲート」を用いて記載した処理は、UIスレッドで処理される
(一旦ワーカースレッドが中断され、UIスレッドに戻る)
┗スレッド内に、UIのコントロールにアクセスする箇所が複数ある場合、複数個所に
「デリゲート」の記載が必要
┗「デリゲート」は本来行いたい処理とは関係無い。関係無い記載があちこちにあるのは
可読性を悪化させる
5.「デリゲート」を用いなくとも、コントロールにバインド済みの変数の値を更新することに
より、バインド変数を介してコントロールへアクセスすることができる
6.一番新しい実現方法はTaskクラスを用いた方法であるため、可能ならばTaskクラスを
使用する
サンプルプログラム①の概要
・「スレッド実行していません」を押すとスレッド処理を開始する
・「中断」を押すとスレッド処理を停止する。押されるまではスレッド内のループ処理を繰り返す
・中断後、スレッド内で「デリゲート」を用いてコントロールへアクセスする
サンプルプログラム①の画面
●初期画面
●「実行」ボタンを押した後の画面
サンプルプログラム①
public partial class MainWindow : Window
{
private Thread t;
private bool isSuspension;
public MainWindow()
{
InitializeComponent();
}
/// ボタン押下時処理
private void Button_Click(object sender, RoutedEventArgs e)
{
if (Button1.Content.Equals("実行"))
{
t = new Thread(new ThreadStart(SampleThreadAsync));
isSuspension = false;
//コントロールを更新
Label_1.Content = "スレッド実行中!";
TextBox_1.Text = "スレッド実行中!";
Button1.Content = "中断";
t.Start();
} else if(Button1.Content.Equals("中断"))
{
isSuspension = true;
t = null;
//コントロールを更新
Label_1.Content = "スレッド実行していません";
TextBox_1.Text = "スレッド実行していません";
Button1.Content = "実行";
}
}
/// スレッド処理用メソッド
private void SampleThreadAsync()
{
double count;
count = 0;
while (isSuspension == false)
{
//中断ボタンが押されるまで繰り返し実行する処理
Thread.Sleep(1000);
count++;
}
//デリゲート
this.Dispatcher.Invoke((Action)(() => {
//この中の記載はUIスレッドとして処理される
//スレッドからコントロールを更新
Label_2.Content = "スレッド内のループの回数は" + count;
}));
}
}
実行結果①
●「中断」ボタンを押した後の画面
サンプルプログラム②のポイント
1.バインドさせるためのクラス「ApplicationViewModel」を作成する
・変数が更新された場合、UIへ通知するように設定する
┗「INotifyPropertyChanged」を継承
┗プロパティ「PropertyChangedEventHandler」を定義
┗メソッド「OnPropertyChanged」を定義
┗セッター内で「this.OnPropertyChanged(“XXXX”)」を実行
2.UIのコントロールと変数をバインドさせる
┗MainWindow.xamlにバインドの設定をする
┗MainWindow.xaml.csにバインドの設定をする
3.ワーカースレッド内で、バインドした変数を更新する
サンプルプログラム②
●バインドさせるためのクラス「ApplicationViewModel」
using System.ComponentModel;
namespace sampleApp
{
public class ApplicationViewModel : INotifyPropertyChanged
{
private string _LabelText;
public string LabelText
{
get
{
return _LabelText;
}
set
{
_LabelText = value;
this.OnPropertyChanged("LabelText");
}
}
public event PropertyChangedEventHandler PropertyChanged = null;
protected void OnPropertyChanged(string info)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
}
●UIのコントロールと変数をバインドさせる(MainWindow.xaml)
<Window x:Class="sampleApp.MainWindow"
(省略)
Title="MainWindow" Height="176" Width="338" ResizeMode="NoResize">
<Grid>
(省略)
<Label x:Name="Label_2" Content="{Binding LabelText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="15,100,0,0" VerticalAlignment="Top" Width="310" Height="26"/>
</Grid>
●UIのコントロールと変数をバインドさせる(MainWindow.xaml)
ワーカースレッド内で、バインドした変数を更新する
public partial class MainWindow : Window
{
private Thread t;
private bool isSuspension;
private ApplicationViewModel applicationViewModel;
public MainWindow()
{
InitializeComponent();
//コントロールと変数をバインド
applicationViewModel = new ApplicationViewModel();
applicationViewModel.LabelText = "スレッド内のループの回数はXX";
Label_2.DataContext = applicationViewModel;
}
/// ボタン押下時処理
private void Button_1_Click(object sender, RoutedEventArgs e)
{
if (Button_1.Content.Equals("実行"))
{
t = new Thread(new ThreadStart(SampleThreadAsync));
isSuspension = false;
//コントロールを更新
Label_1.Content = "スレッド実行中!";
TextBox_1.Text = "スレッド実行中!";
Button_1.Content = "中断";
t.Start();
}
else if (Button_1.Content.Equals("中断"))
{
isSuspension = true;
t = null;
//コントロールを更新
Label_1.Content = "スレッド実行していません";
TextBox_1.Text = "スレッド実行していません";
Button_1.Content = "実行";
}
}
/// スレッド処理用メソッド
private void SampleThreadAsync()
{
double count;
count = 0;
while (isSuspension == false)
{
//中断ボタンが押されるまで繰り返し実行する処理
Thread.Sleep(1000);
count++;
}
//デリゲートを用いずに、バインド済みの変数を介してスレッドからコントロールを更新
applicationViewModel.LabelText = "スレッド内のループの回数は" + count + "回(デリゲートを用いずに更新)";
}
}
実行結果②
●「中断」を押した後の画面