C#の:変数がnullになるのを待って
-
09-09-2019 - |
質問
ネットの奇数ロックセマンティクスが再び私を盗聴されています。
私はスレッドを起動するよ、ターン内の子スレッドはフォームを開始します。フォームが作成されるまで、親スレッドが待機しなければならない。
私の最初の試みは、フォーム変数を監視するモニタを使用していました
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);
}
...これは、例外がスローされます。 Monitor.Enterは、()フォーム== nullであるため、失敗します。
私は非常に簡単にダミーの整数または何かを(私は実際に私はFormThread変数をcanabalizeと思う)作成することもできますが、よりエレガントな解決策があった場合、私は思っていた。
解決
この場合のためのより良い同期プリミティブます:
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);
}
他のヒント
は、新しいフォームをlanchする別のスレッドを使用しての全体のポイントを削除し、現在のスレッドのスピン待ちを行っていないのか?私は何かを誤解していない限り、あなただけの同期新しいフォームを作成したいです。 (それは別のSTAに常駐する必要が何らかの理由はありますか?)
あなたはメッセージメカニズムとして単一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
を呼び出すまで元のスレッドがロックを保持しています。これは、元の生活に戻っにスレッド、およびリリースパルス、フォームを作成するために、第二のスレッド(すでに開始)をすることができます。元のスレッドは、Form
が存在するだけで後に終了します。
私は 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();
}
EventWaitHandleを渡すための別の方法は、(それがあなたのオブジェクトモデルを乱雑にしないように)、パラメータとして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());
}
フォームがロードされたか否かをフラグに静的ブール値を使用します。 それは原子であるので、ロックは必要ありません。
メインのコードでは、単に
のような何かをwhile(!formRun) { Thread.Sleep(100); }
本当の問題は、なぜあなたはこれをやっているのですか? 通常は、メインスレッドがヘルパーのコードを実行するためのGUIのもの、および二次スレッドを実行したいです。 あなたがそれを必要とする理由を説明している場合、私たちはより良い技術と多分思い付くことができます。