我有一个带有弹出菜单的单线程FLTK应用程序,用Fluid创建。我有一个子类Fl_Gl_Window的类,并实现一个handle()方法。 handle()方法调用一个函数,在右键单击时创建一个弹出窗口。我对其中一个菜单项进行了长时间的操作。我的应用程序为其他目的创建了第二个线程。我使用锁来保护我的主线程和第二个线程之间的一些关键部分。特别是,doLongOperation()使用锁。
我的问题是我可以弹出菜单两次并运行doLongOperation()两次,然后它自己死锁,挂起应用程序。 为什么第一次doLongOperation()没有停止GUI并阻止我第二次启动doLongOperation()?
我可以通过用于禁用有问题的菜单项的标志来避免问题,但我想了解为什么它首先是可能的。
这是代码,当然缩写。希望我已经包含了所有相关的内容。
class MyClass {
void doLongOperation();
};
class MyApplication : public MyClass {
MyApplication();
void run();
void popup_menu();
};
void MyClass::doLongOperation()
{
this->enterCriticalSection();
// stuff
// EDIT
// @vladr I did leave out a relevant bit.
// Inside this critical section, I was calling Fl::check().
// That let the GUI handle a new popup and dispatch a new
// doLongOperation() which is what lead to deadlock.
// END EDIT
this->leaveCriticalSection();
}
MyApplication::MyApplication() : MyClass()
{
// ...
{ m_mainWindowPtr = new Fl_Double_Window(820, 935, "Title");
m_mainWindowPtr->callback((Fl_Callback*)cb_m_mainWindowPtr, (void*)(this));
{ m_wireFrameViewPtr = new DerivedFrom_Fl_Gl_Window(10, 40, 800, 560);
// ...
}
m_mainWindowPtr->end();
} // Fl_Double_Window* m_mainWindowPtr
m_wireFrameViewPtr->setInteractive();
m_mainWindowPtr->position(7,54);
m_mainWindowPtr->show(1, &(argv[0]));
Fl::wait();
}
void MyApplication::run() {
bool keepRunning = true;
while(keepRunning) {
m_wireFrameViewPtr->redraw();
m_wireFrameView2Ptr->redraw();
MyClass::Status result = this->runOneIteration();
switch(result) {
case DONE: keepRunning = false; break;
case NONE: Fl::wait(0.001); break;
case MORE: Fl::check(); break;
default: keepRunning = false;
}
}
void MyApplication::popup_menu() {
Fl_Menu_Item *rclick_menu;
int longOperationFlag = 0;
// To avoid the deadlock I can set the flag when I'm "busy".
//if (this->isBusy()) longOperationFlag = FL_MENU_INACTIVE;
Fl_Menu_Item single_rclick_menu[] = {
{ "Do long operation", 0, 0, 0, longOperationFlag },
// etc. ...
{ 0 }
};
// Define multiple_rclick_menu...
if (this->m_selectedLandmarks.size() == 1) rclick_menu = single_rclick_menu;
else rclick_menu = multiple_rclick_menu;
const Fl_Menu_Item *m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0);
if (!m) return;
if (strcmp(m->label(), "Do long operation") == 0) {
this->doLongOperation();
return;
}
// Etc.
}
答案 0 :(得分:2)
确保您不是从多个线程中调用Fl::wait(...)
。我是否正确地从您的代码推断run()
在自己的线程中执行?
第一次致电Fl::wait()
,例如从主线程中,将捕获并处理第一次右键单击(阻止,正如预期的那样,第一次调用doLongOperation()
时继续);与此同时,第二个线程调用例如Fl::wait(timeout)/Fl::check()
将继续刷新显示 - 并且将在第一个长操作仍在跋涉的同时拦截(和服务)第二次右键单击,调用handle()
(在第二个线程中)。这会出现死锁现象,但我希望在长时间操作完成后,UI将重新开始重绘(通过第二个线程)。
通过在popup_menu()
内记录当前线程ID来验证上述内容。
你应该选择一个线程在一个循环中调用Fl::wait(...)
,你不应该阻止该循环 - 将任何非模态或非UI任务生成为单独的线程。即调用popup_menu()
时,在自己的线程中启动长操作;如果在长操作线程仍在运行时(再次)触发popup_menu()
,则将弹出菜单项标记为已禁用(类似于您的变通方法),或者只是通过一个新参数发出长操作线程的信号。< / p>
答案 1 :(得分:1)
任何机会,你的doLongOperation
做了什么可能需要消息泵(或者APC,某些windows'文件API使用下面这些)才能运行(假设你在Windows上看到这种行为)?例如,如果doLongOperation
尝试更新下面使用SendMessage
的GUI,即使在单线程场景中也会出现死锁。
另外,另一个主题是否已经声明了关键部分?您应该能够在挂起期间中断调试器,并希望看到谁在等待什么。