2009-06-03 10 views
2

.Netの奇妙なロックのセマンティクスが私を再び悩ませています。C#:変数がnull以外になるのを待ちます。

スレッドを起動すると、子スレッドが順番にフォームを開始します。親スレッドは、フォームが作成されるまで待つ必要があります。これは、例外をスロー...

private void OpenForm() 
{ 
    if (FormThread == null) 
    { 
     Monitor.Enter(Form); 
     FormThread = new Thread(FormStub); 
     FormThread.SetApartmentState(ApartmentState.STA); 
     FormThread.Start(); 
     Monitor.Wait(Form); 
     Monitor.Exit(Form); 
    } 
} 

private void FormStub() 
{ 
    Form = new ConnectorForm(); 
    Monitor.Enter(Form); 
    Monitor.PulseAll(Form); 
    Monitor.Exit(Form); 
    Application.Run(Form); 
} 

私の最初の試みは、フォーム変数を監視するモニタを使用することでした。 Form == nullなので、Monitor.Enter()は失敗します。

私は非常に簡単にダミーの整数や何かを作成することができます(私は実際にFormThread変数を変更できます)と思っていますが、より洗練されたソリューションがあるかどうかは疑問でした。

答えて

4

より良い同期:

private ManualResetEvent mre = new ManualResetEvent(false); 

private void OpenForm() 
{ 
    if (FormThread == null) 
    { 
     FormThread = new Thread(FormStub); 
     FormThread.SetApartmentState(ApartmentState.STA); 
     FormThread.Start(); 
     mre.WaitOne(); 
    } 
} 

private void FormStub() 
{ 
    Form = new ConnectorForm(); 
    mre.Set(); 
    Application.Run(Form); 
} 
+0

まさに私が探していたプリミティブなのです。私はSystem.Threadingのもののリストを読んだときにそれを見逃した、明らかに...私の眼球は "イベント"か何かを探していた。 – Thanatos

0

現在のスレッドでスピンウェイトを実行していないと、別のスレッドを使用して新しいフォームをランチしてしまうことがありますか?私が何かを誤解していない限り、新しいフォームを同期して作成したいだけです。 (?それは違うSTAに常駐する必要が何らかの理由がある)

+0

彼が他から彼のメインアプリケーションループを実行するために望んでいるように見えます何らかの理由でスレッド...? – jerryjvl

+0

マイナーな問題は、フォームをサポートしているかどうかにかかわらず、メインスレッドのスレッドモデルについて心配する必要がないことです。大きな利点は、メインスレッドがコントロールを返すようになり、シングルスレッドのように操作を実行できることです。このフォームは別のスレッドに置かれ、ユーザーは楽しまれ、情報を得られます。私の希望は、メインスレッドへのAPIの終了はとても良いことです。 – Thanatos

0

あなたはメッセージメカニズムとして単一object/Monitorを使用する、次のことを試みることができる:

private void OpenForm() 
{ 
    if (FormThread == null) 
    { 
     object obj = new object(); 
     lock (obj) 
     { 
      FormThread = new Thread(delegate() { 
       lock (obj) 
       { 
        Form = new ControllerForm(); 
        Monitor.Pulse(obj); 
       } 
       Application.Run(Form); 
      }); 
      FormThread.SetApartmentState(ApartmentState.STA); 
      FormThread.Start(); 
      Monitor.Wait(obj); 
     } 
    } 
} 

元のスレッドがロックを保持していますそれがMonitor.Waitを呼び出すまで。これは、フォームを作成し、元のスレッドを元気に戻して解放するために、2番目のスレッド(既に開始済み)を可能にします。元のスレッドは、Formが存在する場合にのみ終了します。この場合のプリミティブ

+0

'FromThread.Start'と' Monitor.Wait'の間で 'Monitor.Pulse'が呼び出されるとどうなりますか? – Dialecticus

+0

@Dialecticusは、「パルス」を呼び出すことができる唯一の人物がロックを持っている人であることを覚えています。そしてあなたが 'Wait'を呼び出すまで、そのロックは使用中です。つまり、「それはできません」、またはより明示的に「Monitor.Pulse」を呼び出すスレッドは、ロックを持っていないために例外を取得しようとしています*「 –

0

私はこのような場合のためにAutoResetEventを使用する傾向がある:

private AutoResetEvent _waitHandle = new AutoResetEvent(false); 

private void OpenForm() 
{ 
    Thread formThread = new Thread(FormStub); 
    formThread.SetApartmentState(ApartmentState.STA); 
    formThread.Start(); 
    _waitHandle.WaitOne(); 

    // when you come here FormStub has signaled     
} 

private void FormStub() 
{ 
    // do the work 

    // signal that we are done 
    _waitHandle.Set(); 
} 
-1

はフォームがロードされたか否かのフラグに静的なブール値を使用してください。 アトミックなので、ロックする必要はありません。メインコードで

だけ

while(!formRun) { Thread.Sleep(100); } 

本当の問題のようなものが、なぜあなたはこれをやっているされていますか? 通常は、メインスレッドでGUIを実行し、セカンダリスレッドでヘルパーコードを実行します。 なぜそれが必要なのか説明すれば、もっと良いテクニックを考え出すことができます。 EventWaitHandleを渡す

+0

formRunが揮発性であることを希望し、そうでなければ終了することは保証されません。 –

0

もう一つの方法は、(それがあなたのオブジェクトモデルを乱雑にしない)パラメータとしてFormStubにそれを渡すことです:

static void Main() 
{ 
    Application.EnableVisualStyles(); 
    Application.SetCompatibleTextRenderingDefault(false); 

    EventWaitHandle e = new EventWaitHandle(false, EventResetMode.ManualReset); 
    Thread t = new Thread(FormStub); 
    t.SetApartmentState(ApartmentState.STA); 
    t.Start(e); 
    e.WaitOne(); 
} 

static void FormStub(object param) 
{ 
    EventWaitHandle e = (EventWaitHandle) param; 
    Form f = new Form1(); 

    e.Set(); 
    Application.Run(new Form1()); 
} 
関連する問題