Domanda

Ho un'applicazione presumibilmente a thread singolo FLTK con un menu a comparsa, creata con Fluid. Ho un metodo che sottoclassi Fl_Gl_Window e implementa una maniglia () della classe. Il metodo handle () chiama una funzione che crea una finestra pop-up sul tasto destro del mouse. Ho una lunga operazione che faccio per una delle voci del menu. La mia domanda ha creato un secondo thread per qualche altro scopo. Io uso le serrature per proteggere alcune sezioni critiche tra il mio thread principale e che secondo thread. In particolare, doLongOperation () utilizza le serrature.

Il mio problema è che posso apparire il menu di due volte ed eseguire doLongOperation () due volte, e poi deadlock con se stesso, appeso l'applicazione. Perché non lo fa la prima doLongOperation () stallo la GUI e mi impedisce di partire doLongOperation () una seconda volta?

I può evitare il problema con una bandiera che uso per disattivare la voce di menu incriminato, ma vorrei capire perché è possibile in primo luogo.

Ecco il codice, abbreviato naturalmente. Speriamo che ho incluso tutti i bit rilevanti.

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.

}
È stato utile?

Soluzione

Assicurarsi che non sei chiamando Fl::wait(...) da più di un thread. Ho ragione di dedurre dal codice che viene eseguito run() nel proprio thread?

Una prima chiamata a Fl::wait(), ad esempio dal thread principale, sarebbe catturare ed elaborare il primo destro del mouse (blocco, come previsto, mentre la prima chiamata ad incasso doLongOperation()); nel frattempo, le chiamate di un secondo filo per esempio Fl::wait(timeout)/Fl::check() avrebbe continuato a aggiornare la visualizzazione - e la volontà di intercettazione (e servizio) il secondo tasto destro del mouse, chiamando handle() (nel secondo filo), mentre la prima operazione lunga è ancora arrancava lungo. Ciò darebbe l'aspetto di una situazione di stallo, anche se mi aspetto che l'interfaccia utente riprenderà ridisegnare (attraverso la seconda filettatura) quando entrambi lunghe operazioni completo.

Convalida sopra accedendo all'ID thread corrente all'interno popup_menu().

si dovrebbe scegliere un singolo thread alla chiamata Fl::wait(...) in un ciclo, e non si deve bloccare quel ciclo - le attività di spawn qualsiasi non-modali o non-UI come thread separati. Cioè quando popup_menu() viene chiamato, dare il via alla lunga operazione nel proprio thread; se popup_menu() viene attivata (di nuovo), mentre il filo lunga operazione è ancora in corso, sia contrassegnare la voce di menu a comparsa come disabili (analoga alla vostra soluzione), o semplicemente segnalare il thread operazione a lungo per il riavvio con un nuovo parametro.

Altri suggerimenti

Per caso, fa il tuo doLongOperation fare nulla che potrebbe essere necessario la pompa messaggio (o APC, file di API alcune finestre utilizza questi sotto) per essere in esecuzione (supponendo che si vede questo comportamento su Windows)? Ad esempio, se i tentativi doLongOperation per aggiornare il GUI che usi SendMessage sotto, si otterrà la situazione di stallo anche in uno scenario unico filettato.

Inoltre, non un altro thread hanno la sezione critica già fatto? Si dovrebbe essere in grado di rompere nel debugger durante il blocco e, auspicabilmente, vedere chi è in attesa su quello.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top