我遇到了一个烦人的问题,主要是因为我在 C# 多线程方面的技能水平/经验低。
这是背景。
在我的框架中WaitFormHelper
,我有一个Start()
静态类名为a (这是一个带有自定义消息和进度条的小型加载控件)Close()
Start()
locker
WaitForm
在我当前的项目中,我有一个方法,它启动 a 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()
and方法,也简化了: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
为真(即您可能已经理解,由于某种原因,我无法执行计算,例如“用户在我的表单中输入废话”),执行直接关闭 my WaitForm
. 但是在这种情况下,主线程将在方法触发的线程之前Close
到达该方法并获取locker
对象上的锁。Start()
发生的情况是:Close
获取锁,尝试关闭表单但instance
仍然为空,因为Thread
创建的InitializeCallerThread
仍在等待锁实际创建它。Close
释放锁,InitializeCallerThread
获取它并...显示一个WaitForm
不会关闭的。
现在我知道我可以通过在实际启动 WaitForm 之前测试计算参数是否无效来简单地解决这个问题,但是问题是WaitForm
我们框架的所有应用程序都应该使用它(其中包括 40 多个不同的应用程序使用和在 4 个国家/地区维护),因此理想情况下,我宁愿WaitForm
在所有情况下都看到我的工作。
您对我如何同步它以确保肯定首先调用并执行启动线程有任何想法吗?如您所见,我已经AutoResetEvent
为此使用了 an ,但是如果我在测试前听它,if (instance != null)
我最终会陷入困境。
希望这足够清楚!谢谢!