2009-11-04 25 views
22

Próbuję przenieść kod systemu Windows na system Linux, najlepiej za pośrednictwem bibliotek niezależnych od platformy (np. Doładowanie), ale nie jestem pewien, jak przenieść ten fragment kodu zdarzenia.Międzyplatformowy odpowiednik zdarzeń systemu Windows

Bit kodu zawiera dwa wątki (pozwala wywoływać je A i B). A chce zrobić coś, co tylko B może, więc wysyła B wiadomość, a potem czeka, aż B powie, że to zrobione. W oknach wygląda to mniej więcej tak:

void foo();//thread a calls this 
void bar(HANDLE evt); 

void foo() 
{ 
    HANDLE evt = CreateEvent(0,FALSE,FALSE,0); 
    bCall(boost::bind(&bar, evt)); 
    WaitForSingleObject(evt,INFINITE); 
    CloseHandle(evt); 
} 
void bar(HANDLE evt) 
{ 
    doSomething(); 
    SetEvent(evt); 
} 

Spojrzałem na biblioteki boost :: thread, ale nie wydaje się mieć wszystko, co robi, gdy zamyka widziałem był boost :: condition_variable, ale wydaje się, oznacza to w połączeniu z muteksem, co nie ma miejsca w tym przypadku.

+0

Myślę, że twój specyficzny dla systemu Windows kod używa muteksu pod maską. Po prostu to usuwa. – rmeador

+0

możliwy duplikat zdarzenia [ręcznego resetowania okna pthread] (http://stackoverflow.com/questions/178114/pthread-like-windows-manual-reset-event) – jww

+0

[to pytanie] (http: // stackoverflow .com/questions/4692717/win32-reset-event-like-synchronization-class-with-boost-c) ma również dobre informacje – Loomchild

Odpowiedz

10

Myślę, że dobry, cross-platform równoważne win32 wydarzeń jest boost::condition, więc kod może wyglądać następująco:

void foo() 
{ 
    boost::mutex mtxWait; 
    boost::condition cndSignal; 

    bCall(boost::bind(&bar, mtxWait, cndSignal)); 

    boost::mutex::scoped_lock mtxWaitLock(mtxWait); 
    cndSignal.wait(mtxWait); // you could also use cndSignal.timed_wait() here 
} 

void bar(boost::mutex& mtxWait, boost::condition& cndSignal) 
{ 
    doSomething(); 
    cndSignal.notify_one(); 
} 
+1

Czy muteks naprawdę jest do tego potrzebny? Z pewnością przypomina to fakt, że B może potencjalnie zablokować pasek (jeśli upłynął przedział czasu wątku A między mtxWaitLock i cndSignal.wait). –

+0

druga blokada muteksa nie jest potrzebna. Nie musisz trzymać muteksu, aby wywołać powiadomienie (są sytuacje, w których musisz). –

+0

tak masz rację - będę modyfikować kod – Alan

2

można używać doładowania wątek barrier

#include <boost/thread/thread.hpp> 
#include <boost/thread/barrier.hpp> 
#include <iostream> 

void foo(boost::barrier* b) 
{ 
    std::cout << "foo done" << std::endl; 
    b->wait(); 
} 


int main() 
{ 
    std::cout << "start foo" << std::endl; 
    boost::barrier b(2); 

    boost::thread t(&foo, &b); 
    b.wait(); 
    std::cout << "after foo done" << std::endl; 
    t.join(); 
} 
+1

To działa w niektórych przypadkach, jednak bariera nie ma wartości limitu czasu, która jest używana w kilku miejsca. –

-1

w systemach zgodnych z POSIX można użyć POSIX IPC. Służy do przesyłania komunikatów między procesami/między wątkami. Jeśli dobrze pamiętam, dostępny jest port cygwin.

0

robiłem (lub widział) wszystkie z następujących w różnych czasach na takie rzeczy jak to:

Użyj mutex + zmienna warunek.

Użyj rury, foo utwórz rurę i podaj koniec zapisu na pasku. Pasek następnie pisze do rury, gdy pasek jest gotowy. (Działa to nawet w wielu procesach).

Czy foo ankieta na Boolean (tak, to jest to zły pomysł.)

0

Wygląda jak szukasz gniazda sygnału Mechanizmu. można odnaleźć w:

boost i Qt

zarówno crossplatform.

Qt przykład:

#include <QObject> 

class Counter : public QObject 
{ 
    Q_OBJECT 
public: 
    Counter() { m_value = 0; } 

    int value() const { return m_value; } 

public slots: 
    void setValue(int value); 

signals: 
    void valueChanged(int newValue); 

private: 
    int m_value; 
}; 

Counter a, b; 
QObject::connect(&a, SIGNAL(valueChanged(int)), 
        &b, SLOT(setValue(int))); 

a.setValue(12);  // a.value() == 12, b.value() == 12 
b.setValue(48);  // a.value() == 12, b.value() == 48 

void Counter::setValue(int value) 
{ 
    if (value != m_value) { 
     m_value = value; 
     emit valueChanged(value); 
    } 
} 
+1

Nie sądzę, że mechanizm boost/slot ma jakiś mechanizm oczekiwania –

+0

Ale z sygnałem/slotem nie widzę powodu, aby czekać()? Jeśli coś jest gotowe, po prostu opublikuj wiadomość, każdy zainteresowany klient podejmie odpowiednie działania. Prosty wzór deskryptora obserwatora. – bua

+0

@bua Implementacja metody 'waitForReadyRead' to przynajmniej jeden powód do oczekiwania. W takim przypadku musisz czekać na dane i nie powinieneś blokować zdarzeń QT. – baltazar

14

Wszystkie te odpowiedzi są zbyt skomplikowane, chodźcie na ludzi, nie jest tak trudno.

namespace porting 
{ 
    class Event; 
    typedef Event* Event_handle; 
    static const unsigned k_INFINITE = 0xFFFFFFFF; 

    class Event 
    { 
     friend Event_handle CreateEvent(void); 
     friend void CloseHandle(Event_handle evt); 
     friend void SetEvent(Event_handle evt); 
     friend void WaitForSingleObject(Event_handle evt, unsigned timeout); 

     Event(void) : m_bool(false) { } 

     bool m_bool; 
     boost::mutex m_mutex; 
     boost::condition m_condition; 
    }; 

    Event_handle CreateEvent(void) 
    { return new Event; } 

    void CloseHandle(Event_handle evt) 
    { delete evt; } 

    void SetEvent(Event_handle evt) 
    { 
     evt->m_bool = true; 
     evt->m_cond.notify_all(); 
    } 

    void WaitForSingleObject(Event_handle evt, unsigned timeout) 
    { 
     boost::scoped_lock lock(evt->m_mutex); 
     if(timeout == k_INFINITE) 
     { 
     while(!evt->m_bool) 
     { 
      evt->m_cond.wait(lock); 
     } 
     } 
     else 
     { 
     //slightly more complex code for timeouts 
     } 
    } 

}// porting 

void foo() 
{ 
    porting::Event_handle evt = porting::CreateEvent(); 
    bCall(boost::bind(&bar, evt)); 
    porting::WaitForSingleObject(evt, porting::k_INFINITE); 
    porting::CloseHandle(evt); 
} 

void bar(porting::Event_handle evt) 
{ 
    doSomething(); 
    porting::SetEvent(evt); 
} 

Jest chyba trochę więcej zrobić, aby to w pełni działa jak nie jestem zaznajomiony z semantyki WaitForSingleObject (co się stanie, jeśli dwa wątki zadzwonić go w tym samym czasie, co się stanie, jeśli w tym samym wątku nazywa to dwukrotnie). Jednak rozwiązanie będzie wyglądać bardzo podobnie.

0

Od Boost.Thread wersji 1.47 documentation:

Klasy condition_variable i condition_variable_any zapewnić mechanizm jednego wątku do poczekać na powiadomienia od innego wątku że dany stan stał się prawdą.

4

Ponieważ komentarze są dla mnie zamknięte, musiałem zamieścić swoje komentarze na poprzednie posty jako odpowiedź. Ale właściwie nie odpowiadam.

1) Wystąpił problem z rozwiązaniem @Alan. Podany kod przykładowy działa dobrze. Ale różni się od funkcji zdarzeń systemu Windows. Gdy obiekt zdarzenia Windows jest zestaw, dowolnej liczbie kolejne wywołania do WaitForSingleObject natychmiast zwraca, pokazując, że obiekt jest w stanie sygnalizacji. Jednak z rozwiązaniem doładowania mutex/condition, musi powiadomić o stanie dla każdego połączenia foo(), które tego potrzebuje. To sprawia, że ​​sytuacja jest trudniejsza w przypadku "wieloplatformowej" funkcjonalności zdarzeń Windows. notify_all() również nie może pomóc.

Oczywiście jest to w jakiś sposób rozwiązane w przykładowym kodzie @ deft_code przy użyciu zmiennej boolowskiej. (Mimo, że cierpi on z powodu problemu z kondycją wyścigu, należy rozważyć, czy SetEvent(...) nazywa się martwy po while(!evt->m_bool) i przed evt->m_cond.wait(lock) z osobnego wątku.Może to zostać rozwiązane za pomocą niektórych technik zarządzania warunkami wyścigu, aby dwie instrukcje: while() i wait() atomowy), ale ma swój własny niedociągnięcie.

2) Istnieje również problem z kodem @deft_code „s na wykorzystanie doładowania mutex/condition/bool kombinacja:

obiekty zdarzeń w systemie Windows może być o nazwie, który umożliwia ich użycie do synchronizacji między procesami. Na przykład proces A może utworzyć nazwane zdarzenie i ustawić go w następujący sposób: SetEvent(hFileIsReady). Następnie, bez względu na liczbę procesów oczekujących na ustawienie tego zdarzenia (wywołując w ten sposób WaitForSingleObject(hFileIsReady)), nastąpi natychmiastowe normalne wykonanie, dopóki zdarzenie nie zostanie ponownie zresetowane w procesie A przez ResetEvent(hFileIsReady).

Ale połączenie mutex/condition/bool nie może sobie pozwolić na taką funkcjonalność. Oczywiście możemy użyć boostów named_condition i named_mutex. Co jednak z zmienną binarną, którą musimy sprawdzić przed czekaniem?

8

Można użyć obietnicę i przyszłość, z doładowania wątku:

#include <boost\thread.hpp> 

boost::promise<bool> prom; 

void foo() 
{ 
    auto future = prom.get_future(); 
    auto result = future.wait_for(boost::chrono::milliseconds(1000)); 
    // we get here if (a) 1 second passes or (b) bar sets the promise value 
    if (result==boost::future_status::ready) 
    { 
     /* bar set the promise value */ 
    } 
    if (result==boost::future_status::timeout) 
    { 
     /* 1 second passed without bar setting promise value */ 
    } 
} 

void bar() 
{ 
    prom.set_value(true); 
} 
2

Dla osób zaangażowanych w lub pracuje nad przeniesieniem wielowątkowy natywnego systemu Windows C/C++ kod na Linux/Mac, we've authored an open source (MIT-licensed) library który implementuje zarówno ręczne i automatyczne resetowanie zdarzeń WIN32 na pthreads, łącznie z kompletną implementacją WaitForSingleObject i WaitForMultipleObjects, dzięki czemu jest to jedyny dostępny port WFMO dostępny w systemie Linux/Mac.

pevents is available on GitHub i została poddana próbie w walce i jest używana przez niektóre wielkie nazwiska; jest tam także port pobudzający, który gdzieś się unosi.

Korzystanie z funkcji Pevents znacznie ułatwia przenoszenie kodu z systemu Windows, ponieważ podstawowe paradygmaty znacznie różnią się między platformami Windows i POSIX - chociaż zachęcam wszystkich, którzy piszą wieloplatformowy kod do korzystania z wieloplatformowej biblioteki wieloplatformowej, np. pierwsze miejsce.

+0

Dzięki! Od lat szukam przenośnego rozwiązania WFMO. Szkoda, że ​​standard C++ nie przenosi niczego blisko, AFAIK, boost też nie. Zdecydowanie mam jakiś użytek do twojej biblioteki. –

Powiązane problemy