Porting di windows reset manuale dell’evento su Linux?

C’è una soluzione più semplice nel porting di un evento di reimpostazione manuale di windows in pthread, che una variabile condizionale pthread + pthread mutex + a flag se l’evento è impostato o non impostato?

I pthread sono costrutti di basso livello. No, non esiste un meccanismo più semplice; pthread_cond__* è concettualmente simile a un evento di reset automatico. Stai attento, pthread_cond_wait potrebbe avere wakeups spurie, quindi non dovrebbe mai essere usato senza alcun tipo di flag esterno a prescindere dalla situazione.

Costruire la propria non sarebbe troppo difficile, però.

 #include  #include  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); } 

Questo potrebbe non adattarsi al tuo utilizzo, poiché spesso hai un blocco diverso che vorresti usare al posto di ev->mutex , ma questo è l’essenza di come viene tipicamente usato.

È ansible implementare facilmente eventi di reset manuale con pipe:

l’evento è in stato di triggerszione -> c’è qualcosa da leggere dalla pipe

SetEvent -> write ()

ResetEvent -> read ()

WaitForMultipleObjects -> poll () (o select ()) per la lettura

l’operazione “SetEvent” dovrebbe scrivere qualcosa (ad esempio 1 byte di qualsiasi valore) solo per mettere la pipe in uno stato non vuoto, quindi l’operazione successiva “Wait”, ovvero poll () per i dati disponibili per read non verrà bloccata.

L’operazione “ResetEvent” leggerà i dati scritti per assicurarsi che la pipe sia di nuovo vuota. Il read-end della pipe dovrebbe essere reso non-blocking in modo che il tentativo di resettare (read from) già resettato l’evento (pipe vuota) non blocchi – fcntl (pipe_out, F_SETFL, O_NONBLOCK) Poiché potrebbero esserci più di 1 SetEvents prima ResetEvent, dovresti codificarlo in modo che legga tanti byte quanti sono nella pipe:

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

Si noti che l’attesa dell’evento non viene letta dalla pipe e quindi “l’evento” rimarrà nello stato triggersto fino all’operazione di ripristino.

No, non esiste una soluzione più semplice, ma il codice seguente farà il trucco:

 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); } 

Quando si chiama signal (), l’evento entra nello stato segnalato e tutto il thread in attesa verrà eseguito. Quindi l’evento rimarrà nello stato segnalato e tutto il thread calling wait () non attenderà. Una chiamata a reset () riporterà l’evento allo stato non segnalato.

Il SignalCounter è lì nel caso in cui si effettui un segnale rapido / reset per triggersre i thread in attesa.

Preferisco l’approccio pipe, perché spesso non è necessario solo un evento da attendere, ma più oggetti, ad esempio WaitForMultipleObjects(...) . E con l’uso dei tubi si potrebbe facilmente sostituire la chiamata WaitForMultipleObjects Windows con poll(...) , select , pselect ed epoll .

C’era un metodo leggero per la sincronizzazione del processo chiamato Futex (chiamata di sistema Fast Userspace Locking). C’era una funzione futex_fd per ottenere uno o più descrittori di file per futex. Questo descrittore di file, insieme a possibilmente molti altri che rappresentano file, dispositivi, socket o simili reali potrebbero essere passati per select , eseguire il poll o epoll . Sfortunatamente è stato rimosso dal kernel. Quindi i trucchi dei tubi rimangono l’unica possibilità per farlo:

 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]); 

Penso che gli eventi di Windows siano più simili a un semaforo. Vale a dire per il ripristino automatico, si utilizzerà un semaforo binario e la funzione sem_timedwait ().

Stavamo cercando una soluzione simile per il porting di codice C ++ pesantemente multithreading da Windows a Linux, e abbiamo finito per scrivere una libreria Win32 Events for Linux open source con licenza MIT. Dovrebbe essere la soluzione che stai cercando ed è stata pesantemente controllata per le prestazioni e il consumo di risorse.

Implementa eventi manuali e di reimpostazione automatica e le funzionalità WaitForSingleObject e WaitForMultipleObject .

Noi (divulgazione completa: lavoro presso NeoSmart Technologies) ho scritto una libreria open source (con licenza MIT) chiamata pevents che implementa gli eventi WIN32 manuali e di reimpostazione automatica su POSIX e include sia i cloni WaitForSingleObject che WaitForMultipleObjects. Da allora ha visto qualche adozione (è usata in Steam su Linux / Mac) e funziona abbastanza bene.

Sebbene io personalmente consiglierei di usare i paradigmi di multithreading e segnalazione POSIX quando si codifica su macchine POSIX, pevents ti dà un’altra scelta se ne hai bisogno.