2008-10-07 8 views
11

gibt es eine einfachere lösung bei der portierung eines manuell-reset-ereignisses von windows auf pthread, als eine pthread conditional-variable + pthread mutex + ein flag wenn event gesetzt oder nicht gesetzt?pthread-like windows manuell-reset event

Antwort

17

PThreads sind Low-Level-Konstrukte. Nein, es gibt keinen einfacheren Mechanismus; pthread_cond__* ist konzeptionell ähnlich wie ein Auto-Reset-Ereignis. Seien Sie vorsichtig, pthread_cond_wait haben möglicherweise weckende Wecken, so dass es nie ohne irgendeine Art von externer Flagge unabhängig von der Situation verwendet werden sollte.

Der Aufbau Ihres eigenen wäre jedoch nicht zu schwer.

#include <pthread.h> 
#include <stdbool.h> 

struct mrevent { 
    pthread_mutex_t mutex; 
    pthread_cond_t cond; 
    bool triggered; 
}; 

void mrevent_init(struct mrevent *ev) { 
    pthread_mutex_init(&ev->mutex, 0); 
    pthread_cond_init(&ev->cond, 0); 
    ev->triggered = false; 
} 

void mrevent_trigger(struct mrevent *ev) { 
    pthread_mutex_lock(&ev->mutex); 
    ev->triggered = true; 
    pthread_cond_signal(&ev->cond); 
    pthread_mutex_unlock(&ev->mutex); 
} 

void mrevent_reset(struct mrevent *ev) { 
    pthread_mutex_lock(&ev->mutex); 
    ev->triggered = false; 
    pthread_mutex_unlock(&ev->mutex); 
} 

void mrevent_wait(struct mrevent *ev) { 
    pthread_mutex_lock(&ev->mutex); 
    while (!ev->triggered) 
     pthread_cond_wait(&ev->cond, &ev->mutex); 
    pthread_mutex_unlock(&ev->mutex); 
} 

Dies kann Ihre Nutzung nicht passen, wie Sie oft eine andere Sperre, die Sie anstelle von ev->mutex verwenden wollen würden, aber das ist der Kern, wie ist es in der Regel verwendet.

+1

Vergessen Sie nicht, dass das Windows-Auto-Reset-Ereignis sich daran erinnert, dass es signalisiert wurde und informiert den nächsten Thread, der wartet und sich dann selbst zurücksetzt. Das pthread_cond_signal kann tatsächlich nichts tun, wenn keine Threads warten, so dass das "Ereignis" in diesem Fall nicht aufgetreten zu sein scheint. – ScaryAardvark

+0

+1 Vielen Dank! – ceztko

+0

@ephemient: "Die Funktionen pthread_cond_wait() und pthread_cond_timedwait() werden verwendet, um eine Zustandsvariable zu blockieren. Sie werden mit Mutex aufgerufen, das vom aufrufenden Thread gesperrt ist, oder ein undefiniertes Verhalten führt dazu." Sollten Sie den Mutex nicht vor jedem Aufruf von pthread_cond_wait erfassen? Diese Funktionen geben Mutex atomar frei und bewirken, dass der aufrufende Thread bei der Condition der Bedingung cond blockiert; – user877329

2

Ich denke Windows Events sind eher wie ein Semaphor. I.e. Für das automatische Zurücksetzen würden Sie einen binären Semaphor und die Funktion sem_timedwait() verwenden.

3

Nein es keine einfachere Lösung ist aber der folgende Code wird den Trick:

void LinuxEvent::wait() 
{ 
    pthread_mutex_lock(&mutex); 

    int signalValue = signalCounter; 

    while (!signaled && signalValue == signalCounter) 
    { 
     pthread_cond_wait(&condition, &mutex); 
    } 

    pthread_mutex_unlock(&mutex); 
} 

void LinuxEvent::signal() 
{ 
    pthread_mutex_lock(&mutex); 

    signaled = true; 
    signalCounter++; 
    pthread_cond_broadcast(&condition); 

    pthread_mutex_unlock(&mutex); 
} 

void LinuxEvent::reset() 
{ 
    pthread_mutex_lock(&mutex); 
    signaled = false; 
    pthread_mutex_unlock(&mutex); 
} 

Wenn das Signal() aufgerufen wird, wird das Ereignis geht in signalisiert Zustand und alle warten Thread ausgeführt wird. Dann bleibt das Ereignis im signalisierten Zustand und der gesamte Thread, der wait() aufruft, wartet nicht. Ein Aufruf von reset() setzt das Ereignis zurück in den nicht signalisierten Zustand.

Der signalCounter ist für den Fall, dass Sie ein schnelles Signal/Reset durchführen, um wartende Threads zu aktivieren.

5

Sie können einfache manuelle Reset Ereignisse mit Rohren implementieren:

Ereignis in ausgelöstem Zustand ist -> es etwas aus dem Rohr

SetEvent zu lesen ist -> write()

Resetevent - > read()

WaitForMultipleObjects -> Umfrage() (oder wählen Sie()) zum lesen

die "SetEvent" Operation sollte somethin schreiben g (z.B. 1 Byte eines beliebigen Wertes), um die Pipe in einen nicht-leeren Zustand zu versetzen, so dass eine nachfolgende "Warte" -Operation, dh poll() für Daten, die zum Lesen verfügbar sind, nicht blockiert wird.

Die Operation "ResetEvent" liest die geschriebenen Daten, um sicherzustellen, dass die Pipe wieder leer ist. Das Leseende der Pipe sollte nicht blockierend gemacht werden, so dass das Zurücksetzen (Lesen von) bereits zurückgesetztem Ereignis (leere Pipe) nicht blockiert wird - fcntl (Pipe_out, F_SETFL, O_NONBLOCK) Da es mehr als 1 SetEvents geben kann vor dem Event bestehen, sollten Sie es Code, so dass es so viele Bytes liest, wie es in dem Rohr ist:

char buf[256]; // 256 is arbitrary 
while(read(pipe_out, buf, sizeof(buf)) == sizeof(buf)); 

Beachten Sie, dass für den Fall, wartet aus dem Rohr nicht gelesen und daher das „Ereignis“ bleibt in ausgelöster Zustand bis zum Reset-Vorgang.

3

Ich bevorzuge die Rohr-Ansatz, weil oft nicht nur ein Ereignis warten muss, aber mehrere Objekte, z. WaitForMultipleObjects(...).Und mit Verwendung von Rohren könnte man leicht die Windows WaitForMultipleObjects Anruf mit poll(...), select, und epoll ersetzen.

Es gab eine leichte Methode für die Prozesssynchronisierung Futex (Fast Userspace Locking Systemaufruf). Es gab eine Funktion futex_fd, um einen oder mehrere Dateideskriptoren für futex zu erhalten. Dieser Dateideskriptor könnte zusammen mit möglicherweise vielen anderen, die reale Dateien, Geräte, Sockets oder dergleichen darstellen, an select, poll oder epoll übergeben werden. Leider wurde vom Kernel entfernt. So sind die Rohre Tricks bleiben die einzige Einrichtung, dies zu tun:

int pipefd[2]; 
char buf[256]; // 256 is arbitrary 
int r = pipe2(pipefd, O_NONBLOCK); 

void setEvent() 
{ 
    write(pipefd[1], &buf, 1); 
} 

void resetEvent() { while(read(pipefd[0], &buf, sizeof(buf)) > 0) {;} } 

void waitForEvent(int timeoutMS) 
{ 
    struct pollfd fds[1]; 
    fds[0].fd = pipefd[0]; 
    fds[0].events = POLLRDNORM; 
    poll(fds, 1, timeoutMS); 
} 

// finalize: 
close(pipefd[0]); 
close(pipefd[1]); 
+0

+1 Ich würde +10 wählen, wenn ich könnte! –

1

Wir waren für eine ähnliche Lösung zu portieren suchen einige stark multithreaded C++ Code von Windows auf Linux, und am Ende eine Open-Source-Schreiben, MIT-Lizenz Win32 Events for Linux library. Es sollte die Lösung sein, die Sie suchen, und wurde stark auf Leistung und Ressourcenverbrauch überprüft.

Es implementiert manuelle und automatische Rücksetzereignisse sowie die Funktionen WaitForSingleObject und WaitForMultipleObject.

+0

Das sieht sehr gut aus. Es ist schade, dass nix-Deskriptoren, Semaphore, Events, Threads, Sockets usw. nicht einheitlich sind wie bei Windows, wo ein Aufruf sie alle beherrscht. – jww

0

Wir (die vollständige Offenlegung: Ich bei NeoSmart Technologies arbeiten) schrieb eine Open-Source (MIT-Lizenz) Bibliothek pevents die implements WIN32 manual and auto-reset events auf POSIX genannt und umfasst sowohl WaitForSingleObject und WaitForMultipleObjects Klone. Es hat seitdem einige Adoptionen gesehen (es wird in Steam unter Linux/Mac verwendet) und funktioniert ziemlich gut.

Obwohl ich Ihnen persönlich empfehlen würde, POSIX-Multithreading- und Signalisierungs-Paradigmen beim Codieren auf POSIX-Maschinen zu verwenden, gibt Ihnen pevents eine andere Wahl, wenn Sie sie brauchen.