4

Ich brauche einen Mechanismus erinnert an Win32 Reset-Ereignisse, die ich über Funktionen mit der gleichen Semantik mit WaitForSingleObject() und WaitForMultipleObjects() (nur die ..SingleObject() Version für der Moment) . Aber ich ziele auf mehrere Plattformen ab, so dass ich nur boost :: threads (AFAIK) habe. Ich habe mir die folgende Klasse ausgedacht und wollte nach den möglichen Problemen fragen und ob es der Aufgabe gewachsen ist oder nicht. Danke im Voraus.Win32 Reset-Ereignis wie Synchronisationsklasse mit Boost C++

class reset_event 
{ 
bool flag, auto_reset; 
boost::condition_variable cond_var; 
boost::mutex mx_flag; 

public: 
reset_event(bool _auto_reset = false) : flag(false), auto_reset(_auto_reset) 
{ 
} 

void wait() 
{ 
    boost::unique_lock<boost::mutex> LOCK(mx_flag); 
    if (flag) 
    return; 

    cond_var.wait(LOCK); 
    if (auto_reset) 
    flag = false; 
} 

bool wait(const boost::posix_time::time_duration& dur) 
{ 
    boost::unique_lock<boost::mutex> LOCK(mx_flag); 
    bool ret = cond_var.timed_wait(LOCK, dur) || flag; 
    if (auto_reset && ret) 
    flag = false; 

    return ret; 
} 

void set() 
{ 
    boost::lock_guard<boost::mutex> LOCK(mx_flag); 
    flag = true; 
    cond_var.notify_all(); 
} 

void reset() 
{ 
    boost::lock_guard<boost::mutex> LOCK(mx_flag); 
    flag = false; 
} 
}; 

Beispiel Verwendung;

reset_event terminate_thread; 

void fn_thread() 
{ 
while(!terminate_thread.wait(boost::posix_time::milliseconds(10))) 
{ 
    std::cout << "working..." << std::endl; 
    boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); 
} 

std::cout << "thread terminated" << std::endl; 
} 

int main() 
{ 
boost::thread worker(fn_thread); 

boost::this_thread::sleep(boost::posix_time::seconds(1)); 
terminate_thread.set(); 

worker.join(); 

return 0; 
} 

EDIT

ich den Code nach Michael Burr Vorschläge behoben haben. Meine "sehr einfachen" Tests zeigen keine Probleme an.

class reset_event 
{ 
    bool flag, auto_reset; 
    boost::condition_variable cond_var; 
    boost::mutex mx_flag; 

public: 
    explicit reset_event(bool _auto_reset = false) : flag(false), auto_reset(_auto_reset) 
    { 
    } 

    void wait() 
    { 
     boost::unique_lock<boost::mutex> LOCK(mx_flag); 
     if (flag) 
     { 
      if (auto_reset) 
       flag = false; 
      return; 
     } 

     do 
     { 
      cond_var.wait(LOCK); 
     } while(!flag); 

     if (auto_reset) 
      flag = false; 
    } 

    bool wait(const boost::posix_time::time_duration& dur) 
    { 
     boost::unique_lock<boost::mutex> LOCK(mx_flag); 
     if (flag) 
     { 
      if (auto_reset) 
       flag = false; 
      return true; 
     } 

     bool ret = cond_var.timed_wait(LOCK, dur); 
     if (ret && flag) 
     { 
      if (auto_reset) 
       flag = false; 

      return true; 
     } 

     return false; 
    } 

    void set() 
    { 
     boost::lock_guard<boost::mutex> LOCK(mx_flag); 
     flag = true; 
     cond_var.notify_all(); 
    } 

    void reset() 
    { 
     boost::lock_guard<boost::mutex> LOCK(mx_flag); 
     flag = false; 
    } 
}; 

Antwort

3

Ein paar Dinge, die Sie/fix wollen würden überprüfen (Anmerkung - ich sagte keineswegs, dass diese die einzigen Dinge sind - ich habe nur einen kurzen Blick hat):

  • in Ihrer wait() Funktion, Sie setze nicht auf einem bereits signalisiert Ereignis, wenn es sich für auto_reset set:

    void wait() 
    { 
        boost::unique_lock<boost::mutex> LOCK(mx_flag); 
        if (flag) { 
        if (auto_reset) flag = false; // <-- I think you need this 
        return; 
        } 
    
        cond_var.wait(LOCK); 
        if (auto_reset) 
        flag = false; 
    } 
    
  • in wait(const boost::posix_time::time_duration& dur) sollten Sieüberprüfenvor warten auf die Zustandsvariable.

  • in beiden wait Funktionen, wenn Sie auf die Variable Bedingung warten, müssen Sie möglicherweise die Markierung erneut überprüfen, um sicherzustellen, dass ein anderer Thread das Ereignis in der Zwischenzeit nicht zurückgesetzt hat. Dies gilt insbesondere für Auto_Reset-Ereignisse, die nur einen einzelnen Kellner freigeben sollten, selbst wenn mehrere Threads auf das Ereignis warten.

+0

Vielen Dank Michael. Kannst du den letzten Punkt ein bisschen mehr erklären? – fgungor

1

Hier ist meine Version, die etwas optimiert wurde, um das Folgende zu erreichen.

  • Kein Blockieren des Producers mit set(), reset() usw. Stattdessen zählt es die Anzahl der "Releases" anstatt das 1: 1 Mapping zu verlieren, nachdem die boolesche Bedingung wahr ist.
  • Ermöglicht externe Anrufer den Mutex für Wartezeit zu spezifizieren(), ist es oft eine externe Ressource sowieso in meinen Einsatzszenarien und kann aus dem internen Mutex getrennt sein.
  • Verschoben set(), reset_one(), RESET ALL() ruft zu einer internen Mutex, jetzt nicht blockieren sie, wenn sie wiederholt die Verbraucher Anrufe warten aufgerufen, bevor().
  • Jetzt kann mein Lade-Thread mehrere langlebige Anfragen in die Warteschlange stellen, ohne irgendwelche Aufgaben während der Verarbeitung abzubrechen.

    Progression in meinem Projekt verwenden ....

    Boost-Zustand Variable: -> 3 Ladeanforderungen senden, ist Thread beschäftigt und sieht nur 1 oder 2
    Posted Antwort mit Bool: - > sende 3 Ladeaufträge, Erzeugerbausteine ​​bei 2. Anforderung wegen geteilter Mutex.Der Hersteller wird nicht freigegeben, bis die erste Ladeanforderung verarbeitet wurde.
    Meine Version - da draußen> 3 Ladeanforderungen senden, Hersteller sofort von allen 3 zurückkehrt, Verbraucher sieht 3 Lastanforderung langsam aber sicher :)

    Hoffentlich es jemand hilft.

     
    
        class cNonLossyCondition 
        { 
         bool flag, auto_reset; 
         boost::condition_variable cond_var; 
         int lost_signals; 
         boost::mutex internal_mutex; 
    
        public: 
         cNonLossyCondition(bool _auto_reset) 
         { 
          this->flag = false; 
          this->auto_reset = auto_reset; 
          this->lost_signals = 0; 
         } 
    
         void wait(boost::mutex* mx_flag) 
         { 
          boost::unique_lock LOCK(*mx_flag); 
          if (flag) 
          { 
           if (auto_reset) 
            this->reset_one(); 
           return; 
          } 
    
          do 
          { 
           cond_var.wait(LOCK); 
          } while(!flag); 
    
          if (auto_reset) 
           this->reset_one(); 
         } 
    
         bool wait(boost::mutex* mx_flag,const boost::posix_time::time_duration& dur) 
         { 
          boost::unique_lock LOCK(*mx_flag); 
          if (flag) 
          { 
           if (auto_reset) 
            this->reset_one(); 
           return true; 
          } 
    
          bool ret = cond_var.timed_wait(LOCK, dur); 
          if (ret && flag) 
          { 
           if (auto_reset) 
            this->reset_one(); 
    
           return true; 
          } 
    
          return false; 
         } 
    
         void set() 
         { 
          boost::lock_guard LOCK(this->internal_mutex); 
          flag = true; 
          if (this->lost_signals lost_signals = 1; //already incremented 
          } else { 
           this->lost_signals = this->lost_signals + 1; 
          } 
    
          cond_var.notify_all(); 
         } 
    
         void reset_one() 
         { 
          boost::lock_guard LOCK(this->internal_mutex); 
          this->lost_signals = this->lost_signals - 1; 
          if (this->lost_signals lost_signals = 0; 
           flag = false; 
          } 
    
         } 
         void reset_all() 
         { 
          boost::lock_guard LOCK(this->internal_mutex); 
          flag = false; 
          this->lost_signals = 0; 
         } 
        };