سؤال

لديّ تطبيق FLTK ذو الخيال المفرد مع قائمة منبثقة ، تم إنشاؤه باستخدام السائل. لدي فئة تُعد فئة فرعية fl_gl_window وتنفيذ طريقة مقبض (). تستدعي طريقة المقبض () وظيفة تنشئ نافذة منبثقة على النقر بزر الماوس الأيمن. لدي عملية طويلة أقوم بها لأحد عناصر القائمة. قام طلبي بإنشاء موضوع ثانٍ لغرض آخر. أستخدم الأقفال لحماية بعض الأقسام الهامة بين موضوعي الرئيسي وتلك الخيط الثاني. على وجه الخصوص ، يستخدم Dolongoperation () الأقفال.

مشكلتي هي أنه يمكنني منبثقة القائمة مرتين وتشغيل dolongoperation () مرتين ، ثم تطفو مع نفسها ، معلقة التطبيق. لماذا لا تعطل Dolongoperation () واجهة المستخدم الرسومية الأولى وتمنعني من بدء 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.

}
هل كانت مفيدة؟

المحلول

تأكد من أنك لست كذلك الدعوة Fl::wait(...) من أكثر من موضوع واحد. هل أنا على حق في استنتاج من الكود الخاص بك run() ينفذ في موضوعه الخاص؟

أول مكالمة إلى Fl::wait(), ، على سبيل المثال من الخيط الرئيسي ، سوف يصطاد ومعالجة النقر بزر الماوس الأيمن (الحظر ، كما هو متوقع ، في حين أن الدعوة الأولى إلى doLongOperation() العائدات) ؛ في غضون ذلك ، فإن مكالمات الموضوع الثاني إلى EG Fl::wait(timeout)/Fl::check() سيستمر في تحديث الشاشة-وسوف يعترض (والخدمة) النقر بزر الماوس الثاني ، الاتصال بالاتصال handle() (في الخيط الثاني) بينما لا تزال أول عملية طويلة تتجه. هذا من شأنه أن يعطي مظهرًا من الجمود ، على الرغم من أنني أتوقع أن تستأنف واجهة المستخدم إعادة رسم (من خلال الخيط الثاني) عند اكتمال كلتا العمليتين الطويلتين.

التحقق من صحة ما سبق عن طريق تسجيل معرف الخيط الحالي داخل popup_menu().

يجب عليك اختيار موضوع واحد للاتصال Fl::wait(...) في حلقة ، ويجب ألا تمنع هذه الحلقة -تفرخ أي مهام غير وسيطة أو غير UI كمواضيع منفصلة. أي متى popup_menu() يتم استدعاء ، وبدء العملية الطويلة في موضوعها الخاص ؛ إذا popup_menu() يتم تشغيله (مرة أخرى) بينما لا يزال مؤشر ترابط التشغيل الطويل قيد التشغيل ، إما وضع علامة على عنصر القائمة المنبثقة على أنه معطل (مماثل للحلول الخاص بك) ، أو ببساطة الإشارة إلى مؤشر ترابط التشغيل الطويل لإعادة التشغيل بمعلمة جديدة.

نصائح أخرى

بأي فرصة ، هل لديك doLongOperation افعل أي شيء قد يحتاج إلى مضخة الرسائل (أو APCs ، تستخدم بعض واجهة برمجة تطبيقات ملفات Windows هذه أسفل) (على افتراض أنك ترى هذا السلوك على Windows)؟ على سبيل المثال ، إذا كان doLongOperation يحاول تحديث واجهة المستخدم الرسومية التي تستخدم SendMessage تحتها ، ستحصل على الجمود حتى في سيناريو واحد.

أيضا ، هل لدى موضوع آخر القسم الحرج الذي تم المطالبة به بالفعل؟ يجب أن تكون قادرًا على اقتحام تصحيح الأخطاء أثناء تعليقه ونأمل أن نرى من ينتظر ماذا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top