Pregunta

Tengo una aplicación supuestamente único subproceso FLTK con un menú emergente, creado con el fluido. Tengo un método que subclases Fl_Gl_Window e implementa un mango () clase. El método handle () llama a una función que crea una ventana emergente al hacer clic con el botón. Tengo una larga operación que hago para uno de los elementos del menú. Mi solicitud ha creado un segundo hilo para algún otro propósito. Yo uso las cerraduras para proteger algunas secciones críticas entre mi hilo principal y que el segundo hilo. En particular, doLongOperation () utiliza las cerraduras.

Mi problema es que puedo ejecutar el menú dos veces y ejecutar doLongOperation () dos veces, y luego los puntos muertos con sí mismo, colgando la aplicación. ¿Por qué no lo hace la primera doLongOperation () estancar la GUI y me impedir que se inicie doLongOperation () una segunda vez?

Me puede evitar el problema con una bandera que utilizo para desactivar la opción de menú ofender, pero me gustaría entender por qué es posible en el primer lugar.

Este es el código, abreviado por supuesto. Con suerte he incluido todos los bits relevantes.

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.

}
¿Fue útil?

Solución

Asegúrese de que usted no está Fl::wait(...) llamando desde más de un hilo. ¿Tengo razón para inferir a partir del código que se ejecuta run() en su propio hilo?

A primera llamada a Fl::wait(), por ejemplo desde el hilo principal, sería capturar y procesar la primera a la derecha y haga clic (bloqueo, como se esperaba, mientras que la primera llamada al producto doLongOperation()); mientras tanto, las llamadas de una segunda rosca a, por ejemplo, Fl::wait(timeout)/Fl::check() continuaría para actualizar la pantalla - y la intersección voluntad (y servicios) el segundo botón derecho del ratón, llamando handle() (en el segundo hilo), mientras que la primera operación a largo sigue penosamente a lo largo. Esto daría a la aparición de un punto muerto, aunque espero que la interfaz de usuario se reanudaría volver a dibujar (a través del segundo hilo) cuando los dos largas operaciones completa.

Validar lo anterior ingresando el ID del hilo actual dentro popup_menu().

usted debe escoger un solo hilo a Fl::wait(...) llamada en un bucle, y no debe bloquear ese bucle - desove cualquier no-modal o no la interfaz de usuario tareas como hilos separados. Es decir. cuando se llama a popup_menu(), dar comienzo a la operación larga en su propio hilo; si se activa popup_menu() (una vez más), mientras que el hilo largo de la operación todavía está en marcha, ya sea marcar el elemento de menú emergente como discapacitados (análoga a la solución), o simplemente señalar el largo hilo operación para reanudar el juego con un nuevo parámetro.

Otros consejos

Por casualidad, hace su doLongOperation cualquier cosa que pueda necesitar el suministro de mensajes (o APC, API archivo de algunas ventanas utiliza éstos por debajo) a estar en ejecución (asumiendo que usted ve este comportamiento en Windows)? Por ejemplo, si los intentos doLongOperation para actualizar la interfaz gráfica de usuario que utiliza SendMessage debajo, obtendrá el punto muerto, incluso en un escenario de un solo subproceso.

Además, no han otro hilo en la sección crítica ya se ha cobrado? Usted debe ser capaz de romper en el depurador durante la caída y es de esperar ver quién está a la espera de lo que pase.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top