- PR -

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

投稿者投稿内容
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-11-01 20:03
引用:

ひろしさんの書き込み(2005-10-31 11:23)より:

<質問の背景>
コンストラクタ実行中に、排他的ロックがかかる範囲はどこまでなのか?
排他的ロックが解除されるタイミングはいつなのか?


 なぜ、『コンストラクタ実行中に排他ロックがかかる』と考えたのか。なぜ、そのような考えに至ったのか。それこそが質問の背景だと思いますが?


 質問を読み返すと、質問自体はシンプルですよね。

スタート
 ↓
タイマー設定(たとえば10秒後)
何らかの処理(たとえば9秒かかったとする)
ClassA のコンストラクタ実行(たとえば2秒かかるとする)

Q.Timer で指定したメソッドは、
 ClassA コンストラクタが完了してから呼び出されるのか、
 実行中に呼び出されるのか。


 スレッドは、中断可能な点(セーフポイント)にくるまで、中断されることはありません。もし、ClassA のコンストラクタ内にセーフポイントがなければ、別のスレッドに制御が移りませんから、Timer で指定したメソッドは実行されません。セーフポイントがあるなら、実行されます。
 もし、ClassA のコンストラクタ内に Thread.Sleep を記述してテストしたなら、Sleep メソッドは指定時間の間、このスレッドに制御を移さないというメソッドですから、セーフポイントであるわけです。したがって、Timer で指定したメソッドは実行されます。
 この結果を受けて、「コンストラクタ呼び出し中でも実行される」と判断し、それに依存したコードを記述した場合、バグとなる可能性があります。


 セーフポイントは、共通言語ランタイム(CLR)が、ガベージコレクションを安全に実行できる場所のことです。


 それと、.NET Framework には、タイマーが3つあるんですけど、違いは押さえてありますよね?
___________________________________________________________________
□ written by Jitta on 2005/11/01
□ Microsoft MVP :Visual Developer ASP/ASP.NET Oct.2004-Sept.2006
_________________
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2005-11-01 20:15
引用:

Jittaさんの書き込み (2005-11-01 20:03) より:
 スレッドは、中断可能な点(セーフポイント)にくるまで、中断されることはありません。もし、ClassA のコンストラクタ内にセーフポイントがなければ、別のスレッドに制御が移りませんから、Timer で指定したメソッドは実行されません。セーフポイントがあるなら、実行されます。


こ、これはちょっと違いません?(今回のような話とは)
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2005-11-02 18:39
ご回答およびご議論ありがとうございます。
皆さんの議論を拝見しながら自分も考えているのでが、未だ頭が混乱しています。
(1) インスタンスaがnewで初期化中に、外部からaの内部を見る方法はそもそも無いのでしょうか?
(2) 更に頭が混乱するのは、未定義のオブジェクトがインスタンス化される場合と、
  既にインスタンス化されているものが再初期化され、再インスタンス化される場合と考えられますが、
  その場合、デストラクタも挙動も関係してきます。コンストラクタ実行中は廃棄前のゴミインスタンス
  を参照してしまうのでしょうか。
また、コンストラクタの振る舞いについて詳細かつ正確な記述はどこにあるのでしょうか。
(市販の書籍からでもかまいませんが…)ご教示いただければありがたいです。
お手数をおかけします。



{
ClassA a;
a = new ClassA1();
a = new ClassA2();
}

public class ClassA
{
public ClassA(){…}
 …
int x;
}

public class ClassA1 : ClassA
{
public classA1(){…とても重い}

}

public class ClassA2 : ClassA
{
public classA1(){…とても重い}

}


囚人
ぬし
会議室デビュー日: 2005/08/13
投稿数: 1019
投稿日時: 2005-11-02 18:59
(1)
結局無い事はないが、頑張らないと無理という事でしょう。無いと思っても差し支えないでしょう。
(2)
至極単純に考えて下さい。
ClassA1() のコンストラクタ実行中に、a をみるとただの null 。
ClassA1() のコンストラクタ実行後に、a をみるとClassA1 のインスタンス 。
ClassA2() のコンストラクタ実行中に、a をみるとClassA1 のインスタンス 。
ClassA2() のコンストラクタ実行後に、a をみるとClassA2 のインスタンス 。

「プログラミング .NET Framework」
http://www.amazon.co.jp/exec/obidos/ASIN/4891003030/250-5566802-0289037
コンストラクタの詳細云々があったかどうかは忘れましたが^^;
結構詳しかったような気がします。


[ メッセージ編集済み 編集者: 囚人 編集日時 2005-11-02 19:00 ]
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-11-02 19:17
なちゃさん:
 あれ?そうですか?

System.Threading.Timer を使用している
引用:

Timer クラス
タイマ デリゲートは、タイマの構築時に指定され、変更することはできません。このメソッドは、タイマを作成したスレッドで実行されず、システムが提供するスレッド プール スレッドで実行されます。


つまり、「別のスレッド」でメソッドが実行される
スレッドの中断点に来なければ、他のスレッドは実行されない=メソッドは実行されない
反対に、中断点にくれば、他のスレッドに処理が移行する可能性がある

と、思ったんですけど。。。?

 もっとも、どこからも参照されていない(コンストラクタが終了していないので、参照が返っていない)メモリをどうやって参照するの?ということとは、別の話ですね。
 また、それ(参照されていないから参照できるはずがない)ことを「排他ロック」とは言わない、でしょうしね。

 やっぱり、なぜ「排他ロックがかかっている」という結論に達したのか、知りたい。どんな使い方をしようとして、どんなことが起こり、何をどうやって調べて、どんな情報を得て、「コンストラクト中は排他ロックがかかっている」となったんだろう?


ついでの情報:
 System.Threading.Timer クラスには、49日問題がありますので、注意してください。現在のところ、Windows 2003 Server Service Pack 1 でのみ、修正されています。パッチはありますが、入手のためには、今のところサポート契約を結ぶ必要があります(ただし、インシデントは消費しない)。→KB890340
___________________________________________________________________
□ written by Jitta on 2005/11/02
□ Microsoft MVP :Visual Developer ASP/ASP.NET Oct.2005-Sept.2006
_________________
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2005-11-03 07:22
引用:

つまり、「別のスレッド」でメソッドが実行される
スレッドの中断点に来なければ、他のスレッドは実行されない=メソッドは実行されない
反対に、中断点にくれば、他のスレッドに処理が移行する可能性がある



セーフポイントは、ガーベジコレクションが安全だ、という意味だと思います。
スレッドの切替はWindowsやUNIXなどでは、いつ起こるかわかりません。

i++;

のようなコードを実行している最中にも切替が起こりうるのでマルチスレッドは大変で、
ユーザーのプログラムから切替を制御できないからこそ、
プログラムが暴走してもシステムが落ちないのです。

引用:

(1) インスタンスaがnewで初期化中に、外部からaの内部を見る方法はそもそも無いのでしょうか?


引用:

(1)
結局無い事はないが、頑張らないと無理という事でしょう。無いと思っても差し支えないでしょう。



私の知る限り、結構このケースはあります。
ソケットや計算をさせたい場合に、スレッドをカプセル化したい時があります。
そういった時、コンストラクタからクラスローカルスレッドやインスタンスローカルスレッドを
開始することがよくあります。

コード:
public class Class1

{
public int a;
public int b;
public Class1()
{
a = 0;
Thread localthr = new Thread( new ThreadStart( ThreadStartPoint ) );
localthr.Start();
Thread.Sleep(100);
b = a;
}

private void ThreadStartPoint() {
a++;
}
}



このコードは、コンストラクタ実行後にbを見ると1が入っていることがほとんどですが、
PCが激烈に重いときは0が入ってます。
コンストラクタ呼び出しの終わっていないクラスのデータメンバに
他のスレッドからアクセスできてますよね?

インスタンスへの参照は、コンストラクタが実行されている時点で、
すでに設定されているのですね。
きちんと制御する場合にはロック機構が必要になります。

そういった訳ですので、ちょっと変わったことをしたいときには
・コンストラクタ・デストラクタの呼び出し順序、
・その際のメモリ割り当て順序、
・スレッドの実行順序、
・ロック機構
などはしっかり理解しておくことをお勧めします。

でないと、原因不明のバグがたくさん…

[ メッセージ編集済み 編集者: れい 編集日時 2005-11-03 07:30 ]
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2005-11-03 09:19
引用:

Jittaさんの書き込み (2005-11-02 19:17) より:
つまり、「別のスレッド」でメソッドが実行される
スレッドの中断点に来なければ、他のスレッドは実行されない=メソッドは実行されない
反対に、中断点にくれば、他のスレッドに処理が移行する可能性がある

と、思ったんですけど。。。?


中断点の話は、スレッドのサスペンド時とかの話で、通常のスレッド切換えは中断点で
なくても起こりえます。
だからこそスレッドと色々な処理のアトミック性とかが問題になってくるんですよね?

引用:

 やっぱり、なぜ「排他ロックがかかっている」という結論に達したのか、知りたい。どんな使い方をしようとして、どんなことが起こり、何をどうやって調べて、どんな情報を得て、「コンストラクト中は排他ロックがかかっている」となったんだろう?


これは気になるところですね。

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