なぜ私はシングルスレッドFLTKアプリケーションでデッドロックができますか?
-
26-09-2019 - |
質問
私は流体で作成したポップアップメニュー、とおそらくシングルスレッドFLTKアプリケーションを持っています。私は、クラス、サブクラスFl_Gl_Windowおよび実装ハンドル()メソッドを持っています。ハンドル()メソッドは、右クリックでポップアップウィンドウを作成する関数を呼び出します。私は、メニュー項目のいずれかのために行うことを長い操作を持っています。私のアプリケーションは他の目的のための第二のスレッドを作成しました。私は私のメインスレッドと、第2のスレッドの間にいくつかの重要なセクションを保護するためにロックを使用しています。特に、doLongOperation()はロックを使用します。
私の問題は、私は2回押してメニューをポップアップし、二回doLongOperation()を実行できるということであり、それは、アプリケーションをぶら下げ、それ自身でデッドロック。最初doLongOperationをしない理由()の GUIを停止し、doLongOperation()は、第2の開始時間から私を防ぐ?の
私は怒らメニュー項目を無効にするために使用することをフラグで問題を回避することができますが、私はそれが最初の場所で可能である理由を理解したいと思います。
ここでは、もちろん省略コードは、です。うまくいけば、私は、関連するすべてのビットを用意しました。
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.
}
解決
あなたがいないことを確認してくださいを複数のスレッドからFl::wait(...)
を呼び出す。のアムI右のあなたのコードから推測でそれ自身のスレッドでrun()
の実行?
Fl::wait()
の最初の呼び出し、例えばメインスレッドから、(予想通りdoLongOperation()
進行の最初の呼び出しながら、ブロッキング)最初の右クリックをキャッチし、処理します。その間に、第二のスレッドの呼び出しに例えば最初の長い操作はまだ一緒に重そうに歩くされている間(第2のスレッドで)Fl::wait(timeout)/Fl::check()
を呼び出して、第2の右クリックをし、傍受されます(サービス) - handle()
は、表示を更新していきます。私は、UIの時に両方の長い操作の完全な(第2のスレッド経由)再描画を再開することを期待するが、これは、デッドロックの外観を与えるだろう。
の検証popup_menu()
内部現在のスレッドIDをログに記録することによって、上記
のあなたは、ループ内で呼び出しFl::wait(...)
に単一のスレッドを選択する必要があり、あなたはそのループをブロックしてはならない - 別のスレッドなどのspawn任意の非モーダルまたは非UIの作業を。即ちpopup_menu()
が呼び出されたときに、独自のスレッドで長い動作のキックオフ。長い操作スレッドがまだ(あなたの回避策に類似)無効とポップアップメニュー項目をマーク、または単に新しいパラメータで再起動するために長い動作スレッドを信号のいずれか、実行されている間。
popup_menu()
は(再び)トリガされた場合 他のヒント
万が一、あなたのdoLongOperation
はメッセージポンプが必要になる場合があります何でもやるん実行されている(またはAPCを、いくつかのWindowsのファイルAPIは、これらの下には、使用しています)(Windowsでこの動作を参照してくださいと仮定した場合)? doLongOperation
は用途が下SendMessage
というGUIを更新しようとした場合たとえば、あなたも、シングルスレッド化のシナリオでデッドロックを取得します。
また、別のスレッドがクリティカルセクションでは、すでに主張しているのでしょうか?あなたがハングアップ時にデバッガに分解し、うまくいけば何が待っているユーザーを確認することができる必要があります。