C# 入門 & 実践 / C sharp
501-5. Monitorで同期管理
まずは、前回と同じで
lock ()
Interlocked
を使用せずに
Moniter
を利用した形に書き換えます。
private void counter(object sleep_o )
{
int sleep_i = (int)sleep_o;
for (int i = 0; i < 10; i++)
{
// モニター開始!
Monitor.Enter(this);
try
{
Thread.Sleep(sleep_i);
count++;
res += Thread.CurrentThread.Name + " : " + count + "\r\n";
}
finally
{
Monitor.Exit(this);
}
}
}
こうすると501-4と同じ結果が得られます。
T1 counter : 1
T2 counter : 2
T1 counter : 3
T1 counter : 4
T2 counter : 5
T1 counter : 6
T2 counter : 7
T1 counter : 8
T1 counter : 9
T2 counter : 10
T1 counter : 11
T1 counter : 12
T2 counter : 13
T1 counter : 14
T1 counter : 15
T2 counter : 16
T2 counter : 17
T2 counter : 18
T2 counter : 19
T2 counter : 20
次は、waitを入れてみます。
wait で中断したら、 pulse で通知が来ないと復帰できないというリスク?があります・・・
Pulse で通知して、 Exit で終了した瞬間、waitで待っているのもがスタートします。
T3 を待ち状態にしておき、 count が6になったら通知します。
// ここでテストしてみます。
private void goThread()
{
Thread t1 = new Thread(new ParameterizedThreadStart(counter));
t1.Name = "T1 counter";
Thread t3 = new Thread(new ThreadStart(try_wait));
t3.Name = "T3 trywait";
// T2 を先に開始して待ちます。
t1.Start(1);
t3.Start();
// 終わるまで待つ。
t1.Join();
t3.Join();
this.textBox1.Text = res;
}
private void counter(object sleep_o )
{
int sleep_i = (int)sleep_o;
for (int i = 0; i < 10; i++)
{
// モニター開始!
Monitor.Enter(this);
try
{
Thread.Sleep(sleep_i);
count++;
res += Thread.CurrentThread.Name + " : " + count + "\r\n";
if (count == 6)
{
// 6 になったら通知してみます!
// 他スレッドへ通知
Monitor.Pulse(this);
//Thread.Sleep(50);
}
}
finally
{
Monitor.Exit(this);
}
}
}
// 待ちを入れてみる。
private void try_wait()
{
Monitor.Enter(this);
try
{
res += Thread.CurrentThread.Name + " : 待ちます!\r\n";
Monitor.Wait(this);
res += Thread.CurrentThread.Name + " : 通知キタ!\r\n";
}
finally
{
Monitor.Exit(this);
}
}
結果は
T1 counter : 1
T1 counter : 2
T3 trywait : 待ちます!
T1 counter : 3
T1 counter : 4
T1 counter : 5
T1 counter : 6
T1 counter : 7
T3 trywait : 通知キタ!
T1 counter : 8
T1 counter : 9
T1 counter : 10
こうなりました。
6の後に来ていないのは、通知を受けるより早く、7が始まってしまったと言うことでしょう。
試しにPulseをコメントにしてみると・・・終了しません・・・
これどうしましょう?
そんなときは、時間指定をしてタイムアウトしたらエラーにしましょう。
Pulseはコメントにして、
T1 は 10 ミリ秒ごとに設定
T3 のタイムアウトは、30 ミリ秒に設定
// 待ちを入れてみる。
private void try_wait()
{
Monitor.Enter(this);
try
{
res += Thread.CurrentThread.Name + " : 待ちます!\r\n";
bool wait_ret = Monitor.Wait(this, 30);
count++;
if (wait_ret)
{
res += Thread.CurrentThread.Name + " : " + count + "通知キタ!\r\n";
}
else
{
res += Thread.CurrentThread.Name + " : " + count + ">。<;アウト!\r\n";
}
}
finally
{
Monitor.Exit(this);
}
}
T1 counter : 1
T3 trywait : 待ちます!
T1 counter : 2
T1 counter : 3
T1 counter : 4
T3 trywait : 5>。<;アウト!
T1 counter : 6
T1 counter : 7
T1 counter : 8
T1 counter : 9
T1 counter : 10
こんな感じで、完璧ですね。Pulse が来たら true を返します。
これを駆使してマルチスレッドをがんがん使いましょう。
ファイル書き込みや通信、DB処理何にでも使えますね。
データクラスを作成して、更新は全てそこを通すと言う感じで作成したら良いようです。
Monitor の問題点
finally で Monitor.Exit(this)
で終わりにしていましたが、例外が出た場合、駄目ですね
try{
// 0
}catch(Exception e){
// 1
throw e;
}finally{
// 2
Monitor.Exit(this);
}
こうすると例外が発生すると2にいかなくなるので
try{
// 0
Monitor.Exit(this);
}catch(Exception e){
// 1
Monitor.Exit(this);
throw e;
}
こうしないといけないようです!気をつけましょう。
501-4. ParameterizedThreadStart でパラメータを渡す! «
マルチスレッド同期 »
501-6. スレッドセーフな呼び出し!
C# 入門 & 実践 / C sharp