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

<C#> スレッド呼び出し元をブロックするManualResetEventクラスの活用方法 No.2

 --- スレッドの処理が終了するまで、アプリケーションをブロックする場合。


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


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

 

「スレッド呼び出し元をブロックするManualResetEventクラスの活用方法 No.1

 --- スレッドの処理が終了するまで、アプリケーションをブロックする場合」

 

について説明します。






総合の目次


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

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





 

はじめに


スレッドの処理が終了するまで、スレッドの呼び出し元をブロックすることが可能です。

すなわち、スレッドと、スレッドの呼び出し元が、タイミングを合わせることが可能です。

これは、ManualResetEventと言うクラスの機能で行なえます。

ここでのサンプルは、スレッドが計算処理を終わるまで、呼び出し元は待機しています。

そして、スレッドの計算処理を終わったら、呼び出し元のブロックを解除します。





サンプルコード


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






本サンプの仕様


本サンプルでは、スレッド生成等の基本的なコードの書き方を紹介することが目的なので、単純な計算(足し算)をして、その計算結果をテキストボックスに表示するだけにしました。
具体的には、まず、テキストボックス 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;


// スレッド呼び出し元をブロックするManualResetEventクラスの活用方法 No.2
//  --- スレッドの処理が終了するまで、アプリケーションをブロックする場合
namespace ManualResetEventSimple2
{

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

 

    //===================================
    // 演算結果格納用クラス
    //===================================
    public class ThreadAnswer
    {
        public float answer; // 演算結果格
    }

}



 

 

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;


// スレッド呼び出し元をブロックするManualResetEventクラスの活用方法 No.2
//  --- スレッドの処理が終了するまで、アプリケーションをブロックする場合
namespace ManualResetEventSimple2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

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

 

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


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


        // 待機中のスレッドを管理するオブジェクト
        public static System.Threading.ManualResetEvent ManualEvent = new System.Threading.ManualResetEvent(false);


        private ThreadParameter ThreadPara; // 演算値1、2を格納するクラス
        private ThreadAnswer ThreadAns; // 計算結果を格納するクラス


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

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


            // 演算結果格納用オブジェクトを生成
            ThreadAns = new ThreadAnswer();

 

            // 演算値を初期設定
            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);


            // ManualEventを非シグナル状態にする
            // すなわち、スレッドの実行指示の状態をリセットして戻す。
            ManualEvent.Reset();


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

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


            // ManualEventがシグナル状態になるまでスレッドをブロック。
            // すなわち、送受信電文処理用スレッドから、実行開始の指示が出るまで待機する。
            ManualEvent.WaitOne();

 


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


            float answer = ThreadAns.answer; // 計算結果を取得


            // 計算結果格納用オブジェクトを解放
            System.Threading.Monitor.Exit(ThreadAns);

 

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

        }

 

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

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


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


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


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

 

            // 長時間計算をしている様に見せ掛けるため待機。
            // (下記コードは、このサンプルでテスト確認するためのもので
            // すので、実際のプログラミングの時には、記述しないで下さい)
            System.Threading.Thread.Sleep(10000);

 

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

            // 加算処理
            ThreadAns.answer = data1 + data2;

            // 計算結果格納用オブジェクトを解放
            System.Threading.Monitor.Exit(ThreadAns);


            // ManualEventをシグナル状態にする。
            // すなわち、接続待ちスレッドのロックを解除して、接続待ちスレッドの
            // 実行再開の許可をする。
            //  --- <プログラミング上の補足説明>
            //      このSet()メソッドは、計算処理等のスレッドで行なう処理が全て
            //      完了した後に実行しています。すなわち、本スレッドの呼び出し
            //      元のプログラムは、スレッドが完了するまで待機しています。そ
            //      待機中は、呼び出し元のプログラムは、ブロックされています。
            ManualEvent.Set();

        }


    }
}

 

 


 

実行結果


計算結果が表示されます。
(本サンプルでは、わざと計算時間がかかるようにしていますので、注意して下さい。スレッドと、スレッドの呼び出し元とが、どのようなタイミングで処理が行なわれているのか確認するためです) 

 

なお、計算中は、アプリケーションソフトの実行が、ブロックされていることを確認して下さい。

すなわち、アプリケーションソフトをマウス操作で移動しようとしても、動きません。

 

 


実行結果





さいごに


特になし。