Frage

Ich habe eine angeblich Single-Threaded FLTK Anwendung mit einem Popup-Menü, mit Flüssigkeit erstellt. Ich habe eine Klasse, die Unterklassen Fl_Gl_Window und implementiert ein Griff () -Methode. Der Griff () -Methode ruft eine Funktion, die ein Popup-Fenster auf der rechten Maustaste erzeugt. Ich habe eine lange Operation, dass ich für einen der Menüpunkte zu tun. Mein Antrag hat ein zweites Gewinde für einen anderen Zweck erstellt. Ich benutze Schlösser einige kritische Abschnitte zwischen meinem Haupt-Thread und dieser zweite Thread zu schützen. Insbesondere doLongOperation () verwendet die Schleusen.

Mein Problem ist, dass ich zweimal das Menü Popup und ausführen können doLongOperation () zweimal, und es Deadlocks dann mit sich selbst, um die Anwendung hängen. Warum nicht die erste doLongOperation () blockiert die GUI und verhindert, dass mich von Start doLongOperation () ein zweites Mal?

kann ich das Problem mit einer Flagge vermeiden, dass ich den störenden Menüpunkt deaktivieren, aber ich mag verstehen, warum es möglich ist, in erster Linie.

Hier ist der Code, abgekürzt natürlich. Hoffentlich habe ich alle relevanten Bits enthalten.

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.

}
War es hilfreich?

Lösung

Vergewissern Sie sich, dass Sie nicht Fl::wait(...) von mehr als einem Thread aufgerufen wird. Bin ich direkt aus dem Code Folgern, dass run() ausführt in einem eigenen Thread?

Ein erster Aufruf an Fl::wait(), z.B. aus dem Haupt-Thread, wäre den ersten Rechtsklick (Blockierung, wie erwartet, während der erste Anruf zu doLongOperation() Erlös) fängt und verarbeiten; in der Zwischenzeit wird ein zweiter Thread Anrufe z.B. Fl::wait(timeout)/Fl::check() würde weiterhin die Anzeige aktualisieren - und wird abfangen (und Service) den zweiten Rechtsklick, handle() Aufruf (in dem zweiten Thread), während der erste lange Betrieb noch stapft entlang. Dies würde das Aussehen eines Deadlock geben, obwohl ich, dass die UI erwarte (durch den zweiten Thread) neu gezeichnet würde wieder aufnehmen, wenn beide lange Operationen abgeschlossen.

Bestätigen Sie die oben durch die aktuelle Thread-ID innerhalb popup_menu() Anmeldung.

Sie sollten einen einzelnen Thread zu Anruf Fl::wait(...) in einer Schleife wählen, und Sie sollten diese Schleife nicht blockieren - Spawn jede nicht-modal oder nicht-UI Aufgaben als separate Threads. D. h wenn popup_menu() aufgerufen wird, beginnt den langen Betrieb in einem eigenen Thread; wenn popup_menu() ausgelöst wird (wieder), während der lange Betrieb Thread noch ausgeführt wird, entweder das Popup-Menü als deaktiviert markiert (analog zu Ihrem Problem zu umgehen) oder einfach signalisiert den langen Betrieb Thread zu Neustart mit einem neuen Parameter.

Andere Tipps

Durch eine Chance, hat Sie doLongOperation alles tun, das die Meldung Pumpe muss möglicherweise (oder APCs, einige Fenster, Datei-API verwendet diese unten) ausgeführt werden (vorausgesetzt, Sie dieses Verhalten unter Windows sehen)? Wenn zum Beispiel der doLongOperation versucht die GUI, dass Anwendungen SendMessage unter aktualisieren, werden Sie die Deadlock auch in einem Singlethread-Szenario erhalten.

Auch hat ein anderer Thread den kritischen Abschnitt bereits beansprucht? Sie sollten im Debugger während des Hanges brechen können, und hoffentlich sehen, wer auf was wartet.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top