*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

C#でスレッド間をMonitorクラスを使って同期をとる。


*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*


「C#でスレッドを作成する方法」における一連の説明(知恵ノート)において、ここでは、

 

「C#でスレッド間をMonitorクラスを使って同期をとる」

 

について説明します。






総合の目次


本ページを含めた関連事項の総合目次です。

http://note.chiebukuro.yahoo.co.jp/detail/n165458





 

はじめに


あるスレッドと別なスレッドの間で、データーのやり取りをすると異常が発生します(うまくデーターが伝わらないことがある)。
そもそもスレッドとは、並列に実行される処理です。
よって、各スレッドは、勝手に(別々に)動作しているので、データーをやり取りすること自体が困難なことです。
そこで、各スレッド間で同期をとる機能(タイミングを合わせる機能)があります。
ここでは、その同期用機能のひとつであるMonitorクラスについて紹介します。


なお、Monitorクラスは、各スレッド間で共有するデーターをアクセスする際にロックし、アクセスが終わればロックを解除すると言うものです。

このようにデーターをロックすることによって、各スレッド間のやり取りで発生する問題を回避します。





サンプルコード


ここで取り上げるサンプルは、なるべく複雑にならない範囲で、実際にスレッドの扱い方を確かめられるものとします。






本サンプの仕様


本サンプルでは、スレッド生成等の基本的なコードの書き方を紹介することが目的なので、単純な計算(足し算)をして、その計算結果をテキストボックスに表示するだけにしました。
具体的には、まず、テキストボックス textBox1 に、ユーザーが演算値1(例えば、7+12 ならば、7の方の値)を入力します。
次に、テキストボックス textBox2 に、ユーザーが演算値2(7+12 の12の方の値)を入力します。
次に、ボタン button1 をクリックすると、上記の入力値から、計算(足し算)を行ないます。
ここで、その計算処理は、スレッドを用意して、そのスレッドで計算をします。
また、演算値1、2の値は、Monitorクラスを使って、計算用スレッドに渡します。

その後計算処理が終わったら、その計算結果をテキストボックス textBox3 に表示します。




フォームデザイン等の前準備


コードを記述する前に、フォームのデザイン作成などの、以下の前準備を行なって下さい。


<プロジェクトの作成>
本サンプルの確認用に、新規にプロジェクトを作成して下さい。
プロジェクトの種類は、「Windowsフォームアプリケーション」です。


<フォームのデザイン>
デザイン画面で、TextBox (テキストボックス) を3個と、 Button (ボタン)を1個貼り付けて下さい。

 

 デザイン画面



<テキストボックスの複数行化>

テキストボックスは、デフォルトでは1行用になっています。
textBox1 と textBox2 は、デフォルト(1行表示用)のままで構いません。
しかし、textBox3 については、複数行の文字を扱いたいので、以下の操作で、テキストボックスを複数行対応にして下さい。
まず、先程貼り付けたテキストボックス textBox3 を選択状態にして下さい(すなわち、デザイン画面上のテキストボックス textBox3 をクリックする)。
そうすると、テキストボックスの右上に、小さな三角形のマークが表示されます。
それをクリックすると Multiline と言う表記のチェックボックスが表示されます。
そのチェックボックスにチェックを入れると、複数行対応になります。
あとは、デザイン画面のチェックボックスをマウス操作で、縦幅を広げて下さい。




テキストボックス三角

 


<イベントプロシージャの作成>

デザイン画面のフォームをダブルクリックして、Form1_Load() メソッドを作って下さい。

また、先程貼り付けたデザイン画面上のボタン「button1」をダブルクリックして、button1_Click() メソッド を作って下さい。


 

 

<クラスを書くためのソースファイルの作成>


[プロジェクト] - [クラスの追加] で、「新しい項目の追加」画面を表示する。
その画面の [ファイル名]欄に任意のファイル名(クラス用ソースファイルのファイル名)を記入する。
[追加]ボタンをクリックする。

 

なお、ここで作成されたソースファイルには、後述しますクラスのコード(「演算値格納用クラスのコード」のコード)を記述します。


 

 

演算値格納用クラスのコード



 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MonitorSimple
{

    //===================================
    // 演算値格納用クラス
    //===================================
    public class ThreadParameter
    {
        public float data1; // 演算値1
        public float data2; // 演算値2
    }

}


 

 

 

Formクラスのコード



 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;


// C#でスレッド間をMonitorクラスを使って同期をとる。
namespace MonitorSimple
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // スレッドからテキストボックスをアクセスすることを指定
            Control.CheckForIllegalCrossThreadCalls = false;
        }

 

        //============
        // メンバー変数


        private System.Threading.Thread ThreadMethodThread;   // スレッド

        private ThreadParameter ThreadPara; // 演算値1、2を格納するクラス


        //============
        // フォーム起動時イベント
        private void Form1_Load(object sender, EventArgs e)
        {

            // 演算値格納用オブジェクトを生成
            ThreadPara = new ThreadParameter();


            // 演算値を初期設定
            textBox1.Text = "7";
            textBox2.Text = "12";

        }

 

        //============
        // スレッド開始用ボタン
        private void button1_Click(object sender, EventArgs e)
        {

            // ユーザー入力された演算値(string型)を数値(float型)に変換
            float data1, data2; // 演算値1、2
            float.TryParse(textBox1.Text, out data1); // 演算値1
            float.TryParse(textBox2.Text, out data2); // 演算値2


            // 演算値格納用オブジェクトのロック/解放待ち
            //  他のスレッドがオブジェクトをアクセスしていない場合 ---> オブジェクトのロック
            //  他のスレッドがオブジェクトをアクセスしている場合   ---> オブジェクトの解放待ち
            System.Threading.Monitor.Enter(ThreadPara);


            ThreadPara.data1 = data1; // 演算値1
            ThreadPara.data2 = data2; // 演算値2


            // 演算値格納用オブジェクトを解放
            System.Threading.Monitor.Exit(ThreadPara);


            // スレッド作成
            //  --- 対象処理用メソッドをスレッド用メソッドとして登録
            ThreadMethodThread = new System.Threading.Thread(AddThreadMethod);

            // スレッドを開始
            ThreadMethodThread.Start();

        }

 

        //============
        // 加算処理スレッド用メソッド
        //  --- 加算処理を行なうメソッドです。なお、本メソッドは、マルチスレッ
        //      ドとして処理が実行されます。
        private void AddThreadMethod()
        {

            // スレッドに行なわせたい処理を記述する。
            // ここでは、加算処理を行なわせるものとする。


            // 演算値格納用オブジェクトのロック/解放待ち
            //  他のスレッドがオブジェクトをアクセスしていない場合 ---> オブジェクトのロック
            //  他のスレッドがオブジェクトをアクセスしている場合   ---> オブジェクトの解放待ち
            System.Threading.Monitor.Enter(ThreadPara);


            // 演算値を取得
            float data1 = ThreadPara.data1; // 演算値1
            float data2 = ThreadPara.data2; // 演算値2


            // 演算値格納用オブジェクトを解放
            System.Threading.Monitor.Exit(ThreadPara);

 

            // 加算処理
            float ThreadAnswer = data1 + data2;


            // 計算結果表示
            textBox3.Text += data1.ToString() + " + " + data2.ToString() + " = " + ThreadAnswer.ToString() + " ";

        }


    }
}




 

実行結果


計算結果が表示されます。
 

 


実行結果





さいごに


特になし。