2

Fluidで作成されたポップアップメニューを備えた、おそらくシングルスレッドのFLTKアプリケーションがあります。Fl_Gl_Window をサブクラス化し、handle() メソッドを実装するクラスがあります。handle() メソッドは、右クリックでポップアップ ウィンドウを作成する関数を呼び出します。メニュー項目の 1 つで長い操作を行っています。私のアプリケーションは、他の目的のために 2 番目のスレッドを作成しました。メイン スレッドとその 2 番目のスレッドの間のいくつかの重要なセクションを保護するために、ロックを使用します。特に、doLongOperation() はロックを使用します。

私の問題は、メニューを 2 回ポップアップして doLongOperation() を 2 回実行すると、それ自体でデッドロックが発生し、アプリケーションがハングすることです。 最初の doLongOperation() が GUI をストールさせず、2 回目の 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.

}
4

2 に答える 2

2

複数のスレッドから呼び出しFl::wait(...)ていないことを確認してください。run()独自のスレッド で実行されるコードから推測するのは正しい ですか?

たとえばメイン スレッドからのへの最初の呼び出しはFl::wait()、最初の右クリックをキャッチして処理します (予想どおり、 への最初の呼び出しがdoLongOperation()進行する間はブロックされます)。その間、2 番目のスレッドによる eg の呼び出しFl::wait(timeout)/Fl::check()は引き続き表示を更新し、2 番目の右クリックをインターセプト (およびサービス) し、handle()(2 番目のスレッドで) 呼び出しますが、最初の長い操作がまだゆっくりと進んでいます。両方の長い操作が完了すると、UI が (2 番目のスレッドを介して) 再描画を再開すると予想されますが、これはデッドロックのように見えます。

内部の現在のスレッド ID をログに記録して、上記を検証しますpopup_menu()

ループ内で呼び出す単一のスレッドを選択する必要がFl::wait(...)あり、そのループをブロックしないでください。非モーダルまたは非 UI タスクを個別のスレッドとして生成します。popup_menu()つまり、呼び出されたときに、独自のスレッドで長い操作を開始します。長い操作スレッドがまだ実行されている間に(再び)トリガーされた場合popup_menu()は、ポップアップメニュー項目を無効としてマークするか(回避策と同様)、単に長い操作スレッドに新しいパラメーターで再起動するように信号を送ります。

于 2010-08-12T01:50:34.787 に答える
1

万が一doLongOperation、メッセージポンプ(またはAPC、一部のWindowsのファイルAPIはこれらを下で使用します)を実行する必要があるかもしれない何かをしますか(Windowsでこの動作が見られると仮定します)?たとえば、下doLongOperationを使用するGUIを更新しようとするとSendMessage、シングルスレッドのシナリオでもデッドロックが発生します。

また、別のスレッドにクリティカルセクションがすでに要求されていますか?ハング中にデバッガーに侵入して、誰が何を待っているかを確認できるはずです。

于 2010-08-12T01:39:12.123 に答える