C# マルチスレッドのスキル レベル/経験が低いことが主な原因で、ちょっと厄介な問題が発生しています。
これが背景です。私のフレームワークには、 という名前の静的クラスがあり、これには 2 つの静的メソッドがあります (実際にはもっと多くのことですが、ここでは気にしWaitFormHelper
ません)。
このメソッドは、オブジェクトのロックを取得して作成するスレッドを初期化して開始します。 a (これは、カスタム メッセージとプログレス バーを備えた小さな読み込みコントロールです)Start()
Close()
Start()
locker
WaitForm
現在のプロジェクトでは、 を開始しWaitForm
、計算を実行してから を閉じるメソッドがありますWaitForm
。まったく空想的なものはありません。メソッドは次のようになります。可能な限り単純化しました。
public void PerformCalculations()
{
try
{
WaitFormHelper.Start("Title", "message", false);
if (this.CalculationsParameters.IsInvalid)
{
return;
}
// Perform all those lengthy calculations here
}
// catch whatever exception I may have to catch, we don't care here
finally
{
WaitFormHelper.Close();
}
}
以下は、関連するメソッドと属性を持つメソッドとメソッドであり、簡略化されていますStart()
。Close()
private static Thread instanceCaller;
private static WaitForm instance;
private static AutoResetEvent waitFormStarted = new AutoResetEvent(false);
private static object locker = new object();
/// <summary>
/// Initializes WaitForm to start a single task
/// </summary>
/// <param name="header">WaitForm header</param>
/// <param name="message">Message displayed</param>
/// <param name="showProgressBar">True if we want a progress bar, else false</param>
public static void Start(string header, string message, bool showProgressBar)
{
InitializeCallerThread(showProgressBar, header, message);
instanceCaller.Start();
}
/// <summary>
/// Initializes caller thread for executing a single command
/// </summary>
/// <param name="showProgressBar"></param>
/// <param name="header"></param>
/// <param name="message"></param>
private static void InitializeCallerThread(bool showProgressBar, string header, string message)
{
waitFormStarted.Reset();
instanceCaller = new Thread(() =>
{
lock (locker)
{
instance = new WaitForm()
{
Header = header,
Message = message,
IsProgressBarVisible = showProgressBar
};
waitFormStarted.Set();
}
instance.ShowDialog();
});
instanceCaller.Name = "WaitForm thread";
instanceCaller.SetApartmentState(ApartmentState.STA);
instanceCaller.IsBackground = true;
}
/// <summary>
/// Closes current form
/// </summary>
public static void Close()
{
lock (locker)
{
if (instance != null && !instance.IsClosed)
{
waitFormStarted.WaitOne();
instance.FinalizeWork();
instance.Dispatcher.Invoke(
new Action(() =>
{
instance.Close();
}));
}
}
}
それでは、問題に取り掛かりましょう
これは通常は正常に機能しますが、次の場合を除きthis.CalculationsParameters.IsInvalid
ます。 が true の場合 (つまり、おそらく既にご存じのとおり、「ユーザーがフォームにがらくたを入力する」などの何らかの理由で計算を実行できません)、実行によって my が直接閉じられますWaitForm
。ただし、この場合、メイン スレッドはメソッドに到達し、スレッドがメソッドによって起動される前にオブジェクトClose
のロックを取得します。locker
Start()
Close
ロックを取得し、フォームを閉じようとしますが、フォームを閉じようとしますが、まだinstance
null です。これは、Thread
created inInitializeCallerThread
が実際にロックを作成するのを待っているためです。Close
ロックを解除し、InitializeCallerThread
それを取得して...WaitForm
閉じない a を示します。
WaitForm を実際に開始する前に計算パラメーターが無効かどうかをテストすることで、この問題を簡単に修正できることがわかりましたが、問題は、これWaitForm
がフレームワークのすべてのアプリケーションで使用されることになっていることです (これには、使用される 40 以上の異なるアプリが含まれ、 4 か国で維持されている) ため、理想的にはWaitForm
、すべてのケースで私の作業を確認したいと考えています。
スタータースレッドが最初に確実に呼び出されて実行されるように、これを同期する方法について何か考えはありますか? ご覧のとおりAutoResetEvent
、この件に関しては既に を使用していますが、テストの前にそれを聞くとif (instance != null)
、私の場合はスタックしてしまいます。
これが十分に明確であることを願っています!ありがとうございました!