2008-10-07 14 views

Respuesta

17

P hilos son construcciones de bajo nivel. No, no hay un mecanismo más simple; pthread_cond__* es conceptualmente similar a un evento de reinicio automático. Tenga cuidado, pthread_cond_wait puede tener despertaduras espurias, por lo que nunca debe utilizarse sin algún tipo de indicador externo, independientemente de la situación.

Construir uno propio no sería demasiado difícil, sin embargo.

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

esto puede no adaptarse a su uso, ya que a menudo tendrá una cerradura diferente que te gustaría utilizar en lugar de ev->mutex, pero esta es la esencia de cómo se utiliza normalmente.

+1

No olvide que el evento de reinicio automático de Windows "recordará" que ha sido señalizado e informará el siguiente subproceso que espera y luego se reiniciará. la pthread_cond_signal en realidad no puede hacer nada si no hay subprocesos esperando por lo que el "evento" en este caso parece no haber sucedido. – ScaryAardvark

+0

+1 ¡Muchas gracias! – ceztko

+0

@ephemient: "Las funciones pthread_cond_wait() y pthread_cond_timedwait() se usan para bloquear en una variable de condición. Se llaman con mutex bloqueado por el hilo de llamada o se produce un comportamiento indefinido." ¿No debería capturar el mutex antes de cada llamada a pthread_cond_wait? Estas funciones liberan mutex atómicamente y hacen que la cadena que llama se bloquee en la condición condición cond; – user877329

2

Creo que los eventos de Windows son más parecidos a un semáforo. Es decir. para el reinicio automático, usaría un semáforo binario y la función sem_timedwait().

3

No, no hay ninguna solución fácil, pero el siguiente código hará el truco:

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

Cuando se llama a la señal(), el evento va en estado señalado y todos subproceso en espera se ejecutará. Luego, el evento permanecerá en estado señalado y todas las llamadas a hilos wait() no esperarán. Una llamada a restablecer() devolverá el evento al estado no señalizado.

El signalCounter está ahí en caso de que haga una señal/reinicio rápido para activar los hilos en espera.

5

que se pueden implementar fácilmente eventos manuales de restablecimiento con tubos:

evento está en estado activado -> hay algo para leer desde la tubería

SetEvent -> escritura()

ResetEvent - > read()

WaitForMultipleObjects -> poll() (o seleccionar()) para la lectura de

la operación "SetEvent" tiene que escribir algo g (p. 1 byte de cualquier valor) solo para poner la tubería en un estado no vacío, por lo que la operación "Esperar" posterior, es decir, poll() para los datos disponibles para lectura no se bloqueará.

La operación "ResetEvent" leerá los datos escritos para asegurarse de que la tubería esté vacía nuevamente. El extremo de lectura de la tubería debe ser no bloqueante, de modo que intentar restablecer (leer desde) el evento ya reiniciado (tubería vacía) no bloqueará - fcntl (pipe_out, F_SETFL, O_NONBLOCK) Dado que puede haber más de 1 SetEvents antes de la ResetEvent, debe codificarlo para que se lea la mayor cantidad de bytes que hay en la tubería:

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

Nótese que esperar a que el evento no se lee de la tubería y por lo tanto el "evento" se mantendrá en estado activado hasta la operación de reinicio.

3

Prefiero el enfoque de tubería, porque a menudo uno no necesita solo un evento que esperar, sino varios objetos, p. WaitForMultipleObjects(...).Y con el uso de las tuberías, se podría reemplazar fácilmente la llamada de Windows WaitForMultipleObjects con poll(...), select, pselect y epoll.

Hubo un método ligero para la sincronización de procesos llamado Futex (Llamada al sistema de bloqueo de espacio de usuario rápido). Había una función futex_fd para obtener uno o más descriptores de archivos para futexes. Este descriptor de archivo, junto con posiblemente muchos otros que representan archivos reales, dispositivos, sockets o similares, se pueden pasar a select, poll o epoll. Lamentablemente fue eliminado del kernel. Por lo que los trucos tuberías siguen siendo la única instalación para hacer esto:

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 ¡Yo votaría +10 si pudiera! –

1

Estábamos buscando una solución similar a puerto algunos fuertemente multiproceso código C++ de Windows a Linux, y terminamos de escribir un código abierto, el MIT-licencia Win32 Events for Linux library. Debe ser la solución que está buscando, y ha sido cuidadosamente revisada para el rendimiento y el consumo de recursos.

Implementa eventos manuales y de restablecimiento automático, y las funcionalidades WaitForSingleObject y WaitForMultipleObject.

+0

Esto se ve muy bien. Es muy malo * descriptores nix, semáforos, eventos, hilos, enchufes, etc. no son viables de una manera unificada, como en Windows, donde una llamada los gobierna a todos. – jww

0

Nosotros (la revelación completa: Trabajo en Tecnologías NeoSmart) escribió un código abierto (MIT licencia) biblioteca llamada pevents cuales implements WIN32 manual and auto-reset events en POSIX, e incluye tanto WaitForSingleObject y WaitForMultipleObjects clones. Se ha visto alguna adopción desde entonces (se usa en Steam en Linux/Mac) y funciona bastante bien.

Aunque te aconsejo personalmente que uses los paradigmas POSIX de subprocesamiento múltiple y señalización cuando se codifica en máquinas POSIX, pevents te ofrece otra opción si la necesitas.