- PR -

[C#] コンストラクタの排他ロックの詳細

投稿者投稿内容
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-10-31 11:23
<質問>
クラスをインスタンス化する際に自動的にコンストラクタが呼び出されますが、
(1)(2)(3)の条件を満たしたロジックがあり、new演算子でクラスAをインスタンス化
している最中(つまり重たいコンストラクタを実行中)にたまたま、Timerのこるバック
メソッドが呼び出された場合、コールバックメソッドは直ちに実行されるのでしょうか?
それともコンストラクタの終了を待って実行されるのでしょうか?

<前提条件>
(1) 仮に重たいコンストラクタを実装したクラスAがあるとします。
(2) 同一のスレッドでSystem.Threading.Timerが定義されているとします。
(3) (2)のコールバックメソッドからクラスAをアクセスはしていません。(直接は無関係)

<質問の背景>
コンストラクタ実行中に、排他的ロックがかかる範囲はどこまでなのか?
排他的ロックが解除されるタイミングはいつなのか?
自分の理解があやふやなので、きちんと理解しておきたい。つまり、
排他的ロックがかかる範囲は?例えば下記のどの範囲か?
(1) 初期化される一部のフィールドのみ
(2) コンストラクタを実行中のインスタンス全体
(3) コンストラクタの実行が完了するまでスレッドの切り替えやイベントも含めて入り込めない。
排他的ロックが解除されるタイミングは?例えば下記のどちらか?
(1) フィールドの初期化が完了していれば、コンストラクタの途中であっても外部から使用できる。
(2) コンストラクタが完了するまで、フィールドは使用できない。

※コンストラクタで何が起こるのか詳細を理解したいのですが、分かりやすい解説記事はありますか?

宜しくお願いします。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2005-10-31 11:57
こんにちは。青二才の言う事なので、それなりに聞いて下さい^^;

スレッドの切り替えはいつ起こるか分からない。
コンストラクタもただのメソッド。
データは明示的に排他制御しないと、2つのスレッドからどんなタイミングでも弄り放題。

以上より、
引用:

排他的ロックがかかる範囲は?例えば下記のどの範囲か?


(4)ない。

引用:

排他的ロックが解除されるタイミングは?例えば下記のどちらか?


マルチスレッドやロックに関係なく(2)
コンストラクタの呼び出しが完了しないと、フィールドは当然使用不可。
コンストラクタの呼び出しが完了していないのに、別スレッドからそのインスタンスを使おうとしたらインスタンスがないだけ。

かな?時間があればもう少し詳細に書きたいです。
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-10-31 14:54
ご回答ありがとうございます。

コンストラクタを実行している途中では、
外部からは(生成途中の)インスタンスが単にロックしているように見えるのですね。
ということは、下記(1)(2)(3)の理解で正しいでしょうか。
(1) ロックがかかっているインスタンスをアクセスしない処理は
  同一スレッド(例 Timerのコールバックメソッドやイベント等)、あるいは別スレッドに
  関わり無く何の阻害も受けず実行される。
(2) 生成中のインスタンスにアクセスする処理は待機状態になり、
  コンストラクタが終了した後に目覚めて実行を継続する。
(3) 静的なメンバーについては、インスタンスではないので、
  (1)と同様何の阻害もうけず実行される。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2005-10-31 15:28
いえ、少々違うかと。

(1)について。
正解だと思います。

(2)について。
安全ではありません。
ロックなどかかっていないので、そのインスタンスが生成されている保障がありません。生成途中であるならば、他のスレッドから見れば、生成されていないのと同じ事です。
生成されている事を保障したいののなら、明示的にロックする必要があります。

(3)について。
正解だと思います。
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2005-10-31 15:36
ていうか普通のプログラム書いてる限り、
生成中の参照にアクセスすることなんてできません。

new されて戻ってくる前の(メモリ確保までしか行われていない)参照を、
別スレッドがどうやって見るのか、という話。

--追記
コンストラクタ内からthisをどこかに保存するとかして見ることはできますね。
いずれにしても、自動的に排他処理されたり、ロックされたりなんてことは起こりません。


[ メッセージ編集済み 編集者: なちゃ 編集日時 2005-10-31 15:41 ]
まどか
ぬし
会議室デビュー日: 2005/09/06
投稿数: 372
お住まい・勤務地: ますのすし管区
投稿日時: 2005-10-31 15:44
#横から失礼します。

引用:

生成中のインスタンスにアクセスする


この例がまったく想像できないのですが、
メモリ上に展開されたNewのエントリに複数のものが飛び込むということはあるのですか?
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2005-10-31 15:47
ついでに。
ロックされたオブジェクトへの「アクセス」は別に排他されたりしません。
謎かけみたいですが。
囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2005-10-31 15:50
コード:
class Class1
{
	private static Class2 class2;

	[STAThread]
	static void Main(string[] args)
	{
		Thread thread2 = new Thread( new ThreadStart( StartThread2 ) );
		thread2.Start();
		class2 = new Class2();
	}

	private static void StartThread2()
	{
//			Thread.Sleep(2000);
		Console.Write( class2.A );
	}
}

class Class2
{
	public int A = 0;

	public Class2()
	{
		Thread.Sleep(1000);
		A = 1;
	}
}


みたいな。
Thread.Sleep(2000)のコメントを外すと実行できる(ときがある)。
生成中かどうかは知りませんが。

スキルアップ/キャリアアップ(JOB@IT)