WisdomSoft - for your serial experiences.

7.3 中断と再開

本稿は Suspend() メソッドと Resume() メソッドによるスレッドの中断と再開について解説します。これらのメソッドは互換性のために残されており、現在は使用しません。

7.3.1 スレッドを停止させる

Thread オブジェクトを取得することができれば、スレッドの実行を中断させたり、中断した地点から再開させることができます。この機能によって、あるスレッドから別のスレッドを停止させることができます。本来実行中のコードを外部から強制的に停止させるということをするべきではありませんが、アニメーションを中断させ、後で再開させるなどの利用方法があるかもしれません。

ただし、外部のスレッドから停止させたい対象のスレッドが現在どのようなコードを実行しているか知る手段はありません。スレッドを停止させるにはスレッドが実行している処理内容を理解したうえで、スレッドを停止させても他のコードに影響が及ばないように注意する必要があります。

以下の内容は過去の互換性のために残されているもので、現在は使われません。中断されるスレッドが何を実行しているか確認できないため、タイミングによってデッドロックを起こします。状態に応じてスレッドを停止させるには、排他ロックや Mutex クラスの利用を検討してください。

スレッドを停止させるには Thread クラスの Suspend() メソッドを呼び出します。このメソッドが呼び出された時点で、Thread オブジェクトが実行しているスレッドの実行を中断させます。スレッドが起動されていない状態や、すでに停止している状態の場合は無効です。

Thread クラス Suspend() メソッド
public void Suspend()

一時停止したスレッドは Resume() メソッドから再開することができます。スレッドが中断されていない状態で呼び出した場合は例外が発生します。

Thread クラス Resume() メソッド
public void Resume()

スレッドが現在実行中なのか、Suspend() によって停止している状態なのかどうかを調べるには ThreadState プロパティからスレッドの状態を取得します。

Thread クラス ThreadState プロパティ
public ThreadState ThreadState { get; }

スレッドの状態は System.Threading.ThreadState 列挙体のメンバの組み合わせとなります。

System.Threading.ThreadState 列挙体
[Flags]
[Serializable]
public enum ThreadState

ThreadState 列挙体は FlagsAttribute 属性によってメンバを組み合わせることができます。スレッドを中断しているかどうかは Suspended メンバが設定されています。このほかに、スレッドが停止していることを表す Stopped、スレッドを起動していない状態を表す Unstarted、スレッドが Sleep() メソッドなどによって一時的にブロック状態となっていることを表す WaitSleepJoin メンバなどがあります。

コード1
using System.Threading;
using System.Drawing;
using System.Windows.Forms;

public class Test : Form
{
	private Thread thread;
	private bool addh, addv;
	private Rectangle rect = new Rectangle(0, 0, 32, 32);

	private void Test_Start() 
	{
		while(!IsDisposed) 
		{
			rect.X += addh ? 5 : -5;
			rect.Y += addv ? 5 : -5;

			if (rect.X + rect.Width >= ClientSize.Width) addh = false;
			else if(rect.X <= 0) addh = true;
		
			if (rect.Y + rect.Height >= ClientSize.Height) addv = false;
			else if(rect.Y <= 0) addv = true;

			Invalidate();
			Thread.Sleep(10);
		}
	}

	protected override void OnPaint(PaintEventArgs e)
	{
		base.OnPaint (e);
		Brush brush = new SolidBrush(ForeColor);
		e.Graphics.FillEllipse(brush, rect);
	}

	protected override void OnMouseUp(MouseEventArgs e)
	{
		base.OnMouseUp (e);
		if ((thread.ThreadState & ThreadState.Suspended) != 0) thread.Resume();
		else thread.Suspend();
	}

	public Test() 
	{
		thread = new Thread(new ThreadStart(Test_Start));
		thread.Start();
	}

	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード1 実行結果

コード1は、タイマーを使って実装した「7.1 タイマー コード2」 をスレッドに書き換えたものです。プログラムを実行すると黒い楕円がクライアント領域の内部を移動し続けます。楕円を描画する座標を書き換えてフォームを再描画させているのは Test_Start() メソッドです。このメソッドは、フォームが有効な限り while 繰り返し文を実行して楕円の座標を書き換え続けます。

Test_Start() メソッドを実行しているのは、コンストラクタで作成した新しいスレッドです。この新しいスレッドは private フィールドの thread 変数に保存されています。フォームのクライアント領域を左クリックすると、thread フィールドのスレッドが停止状態かどうかを調べ、停止していなければ Suspend() メソッドを呼び出してスレッドを停止させます。スレッドが停止すると、楕円を移動させていたアニメーションが停止します。

スレッドが中断されている状態で左クリックすると、今度は Resume() メソッドが呼び出されてスレッドが再開します。