我正在使用一个在单独的线程中运行自己的事件调度程序的框架。该框架可能会产生一些事件。
class SomeDataSource {
public event OnFrameworkEvent;
void FrameworkCallback() {
// This function runs on framework's thread.
if (OnFrameworkEvent != null)
OnFrameworkEvent(args);
}
}
我想将这些事件传递给Winforms线程上的Winforms对象。我显然检查InvokeRequired
并在必要时将其发送给Winforms线程。
class SomeForm : Form {
// ...
public void SomeAction(SomeArgs args) {
if (InvokeRequired) {
BeginInvoke(new Action(SomeAction), args);
return;
}
// ...
}
}
现在可以在表单处于关闭状态时传递事件,这会导致各种问题,所以我在Winforms线程上从框架的事件源取消注册表单的事件处理程序,如下所示:
var form = new SomeForm();
var src = new SomeDataSource();
// ...
src.OnFrameworkEvent += form.SomeAction;
form.Closing += (sender, eargs) => src.OnFrameworkEvent -= form.SomeAction;
现在,这种方法是线程安全的吗?如果表单处于关闭状态,而外部线程调用BeginInvoke
,则调用仍会排队表格关闭时执行? (这意味着我仍然有机会遇到同样的问题)
是否有更好的方法或推荐的跨线程事件处理模式?
答案 0 :(得分:4)
不,不是。线程可能只是在您取消注册并关闭表单时执行事件处理程序。小赔率,但不是零。在关闭表单之前必须先停止线程。如果你不想中止它,你必须通过取消FormClosing事件来保持表单打开,然后让线程的完成回调关闭表单。
查看this thread了解详情。
答案 1 :(得分:2)
您可以将此代码添加到构造函数CheckForIllegalCrossThreadCalls = false;
,并且不会抛出任何异常。
答案 2 :(得分:1)
我没有使用具有自己的事件调度程序的框架,但我对自己创建的线程有自己的经验。这是我的经历
这种方法不是线程安全的。即使程序本身已关闭,仍将调用调用。我在任务管理器中看到这个(在程序关闭后如你所说)作为挂线程。 (即使您也从任务管理器中删除该程序。)。我不得不单独杀死那些线程。
当表单关闭时,您必须终止调度程序线程,以便在该线程中发生任何错误时它不会挂起。
form.Closing += (sender, eargs) => src.OnFrameworkEvent -= form.SomeAction;
// pseudo-code (find c# equivalent)
if (dispatcherthread.isrunning)
dispatcherThread.kill();