Почему я могу тупиться в однопоточной приложении FLTK?

StackOverflow https://stackoverflow.com/questions/3416010

  •  26-09-2019
  •  | 
  •  

Вопрос

У меня якобы одноретичное приложение FLTK с всплывающим меню, созданным с жидкостью. У меня есть класс, который подклассы fl_gl_window и реализует метод ручки (). Метод ручки () вызывает функцию, которая создает всплывающее окно на щелчок правой кнопкой мыши. У меня длинная работа, которую я делаю для одного из пунктов меню. Мое приложение создало вторую нить для каких-либо других целей. Я использую блокировки для защиты некоторых критических секций между моим главным потоком и тем вторым потоком. В частности, долонгация () использует замки.

Моя проблема заключается в том, что я смогу всплыть меню дважды и запускать долонгацию () дважды, и затем тупики с самим собой, висит приложение. Почему первая долонгация () не остановит графический интерфейс и не мешает мне начать долонгацию () во второй раз?

Я могу избежать проблем с флагом, который я использую для отключения элемента оскорбления меню, но я хотел бы понять, почему можно в первую очередь.

Вот код, сокращенный конечно. Надеюсь, я включил все соответствующие биты.

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(...) от более чем одного потока. Я правильно вывод из вашего кода, который run() Выполняется в собственном потоке?

Первый звонок Fl::wait(), например, из основного потока, будет уловить и обрабатывать первый щелчок правой кнопкой мыши (блокировка, как ожидалось, а первый звонок doLongOperation() выручка); Тем временем, призывы второго потока, например, Fl::wait(timeout)/Fl::check() будет продолжать обновлять дисплей - и перехватит (и сервис) второй щелчок правой кнопкой мыши, вызывая handle() (во втором потоке), в то время как первая длительная операция все еще пробивается. Это дало бы появление тупика, хотя я ожидаю, что UI возобновит перерисование (через вторую нить), когда оба длинные операции завершаются.

Проверьте вышеизложенное путем ввение текущего идентификатора потока внутри popup_menu().

Вы должны выбрать одну нить, чтобы позвонить Fl::wait(...) в цикле, и вы не должны блокировать эту петлю - порождают любые немодальные или неэйвые задачи как отдельные потоки. То есть когда popup_menu() вызывается, отключается длинная операция в своей поток; если popup_menu() Срабатывает (снова), когда длинный поток операции все еще работает, либо пометьте пункт меню всплывающего меню, как отключено (аналогично вашему обосному обходу), либо просто сигнализируйте на длительный поток операции, чтобы перезапустить с новым параметром.

Другие советы

Случайно, делает ваш doLongOperation Делайте все, что может понадобиться насос (или APCS, некоторые файлы Windows »использует эти внизу), чтобы запустить (при условии, что вы видите это поведение в Windows)? Например, если doLongOperation пытается обновить графический интерфейс, который использует SendMessage Внизу вы получите тупик даже в однопоточной сценарии.

Также есть еще одна нить имеет критический раздел, уже утверждаемый? Вы должны быть в состоянии сломаться в отладчике во время зависания и, надеюсь, увидим, кто ждет, что.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top