2009-11-04 12 views
22

Estoy tratando de portar algún código de Windows a Linux, idealmente a través de librerías independientes de plataforma (por ejemplo, aumentar), sin embargo, no estoy seguro de cómo portar este código de evento.Equivalente multiplataforma a eventos de Windows

El bit de código implica dos hilos (vamos a llamarlos A y B). A quiere hacer algo que solo B puede, por lo que envía un mensaje a B, luego espera que B lo haga. En las ventanas se ve algo como esto:

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

Miré a la biblioteca de impulso :: hilo, pero que aún no ha parecen tener nada que hace esto, se cierra el que podía ver era el impulso :: condition_variable, pero parece eso significa en conjunción con un mutex, que no es el caso aquí.

+0

Creo que su código específico de Windows está utilizando un mutex debajo del capó. Simplemente abstrae eso. – rmeador

+0

posible duplicado de [pthread-windows windows manual-reset event] (http://stackoverflow.com/questions/178114/pthread-like-windows-manual-reset-event) – jww

+0

[esta pregunta] (http: // stackoverflow .com/questions/4692717/win32-reset-event-like-synchronization-class-with-boost-c) también tiene buena información – Loomchild

Respuesta

10

Creo que una buena, multiplataforma equivalente a eventos Win32 es boost::condition, por lo que el código podría ser algo como esto:

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

¿Es realmente necesario el mutex para esto? Ciertamente salgo como el hecho de que significa que B podría bloquear en la barra (si el segmento de tiempo del hilo A expiró entre mtxWaitLock y cndSignal.wait). –

+0

el segundo bloqueo mutex no es necesario. No necesita mantener el mutex para llamar a notify (hay situaciones en las que lo necesitaría). –

+0

sí, tienes razón, modificaré el código – Alan

2

puede utilizar hilo impulso 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

Eso funciona en algunos casos, sin embargo, la barrera carece de un valor de tiempo de espera que se usa en un par de lugares. –

-1

En sistemas POSIX puede utilizar Posix IPC. Se utiliza para mensajería entre procesos/entre hilos. Si no recuerdo mal, hay un puerto cygwin disponible.

0

que he hecho (o visto) todo lo siguiente en varias ocasiones para cosas como esta:

Usar un mutex + una variable de condición.

Use una tubería, después de haber creado la tubería y pase el extremo de escritura a la barra. Bar luego escribe en la tubería cuando la barra está lista. (Esto incluso funciona en múltiples procesos).

Tener sondeo foo en un valor lógico (sí, esto es una mala idea.)

0

Parece que usted está buscando mechanizm-ranura de la señal. Usted puede encontrar uno en:

boost y Qt

tanto multiplataforma.

ejemplo Qt:

#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

No creo que el mecanismo de ranura/señal de refuerzo tenga ningún tipo de mecanismo de espera –

+0

Pero con señal/ranura no veo razón para esperar()? Si algo está listo, simplemente publique el mensaje, cualquier cliente interesado procedería a una acción adecuada. Patrón de diseño de observador simple. – bua

+0

@bua La implementación del método 'waitForReadyRead' es al menos una razón para esperar. En este caso, debe esperar los datos y no debe bloquear los eventos QT. – baltazar

14

Todas estas respuestas son demasiado complejo, vamos gente, no es tan difícil.

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

Probablemente hay un poco más que hacer para conseguir este funcionamiento totalmente ya que no estoy familiarizado con la semántica de WaitForSingleObject (¿Qué pasa si dos subprocesos llaman al mismo tiempo, ¿qué ocurre si el mismo hilo lo llama dos veces). Sin embargo, la solución se verá muy similar a esto.

0

De Boost.Thread versión 1.47 documentation:

Las clases condition_variable y condition_variable_any proporcionan un mecanismo para un hilo a esperar la notificación desde otro hilo que una condición particular, se ha convertido en realidad.

4

Como los comentarios están cerrados para mí, tuve que publicar mis comentarios en publicaciones anteriores como respuesta. Pero en realidad no estoy respondiendo.

1) Hay un problema con la solución de @Alan. El código de muestra que proporcionó funciona bien. Pero es diferente de la funcionalidad de eventos de Windows. Cuando un objeto Evento de Windows es establece, cualquier número de llamadas posteriores al WaitForSingleObject vuelve inmediatamente, mostrando que el objeto está en estado señalizado. Pero con la solución de boost mutex/condition, bar() tiene que notificar la condición para cada foo() llamadas que lo necesiten. Esto hace que la situación sea mucho más difícil para la funcionalidad de eventos de Windows "multiplataforma". notify_all() tampoco puede ayudar.

Por supuesto, esto se resuelve de alguna manera en el código de ejemplo de @dift_code mediante el uso de una variable booleana. (Aunque sufre un problema de condición de carrera. Considere si se llama SetEvent(...) muerto después de while(!evt->m_bool) y antes de evt->m_cond.wait(lock) desde un hilo separado. Se producirá un interbloqueo. Sin embargo, esto puede resolverse utilizando algunas técnicas de gestión de condición de carrera para hacer las dos declaraciones while() y wait() atómica) pero tiene su propia deficiencia:.

2) también hay un problema con el código de @deft_code 's en hacer uso de impulso mutex/condition/bool combinación:

objetos de eventos en Windows puede ser llamado que les permite ser utilizados para sincronizaciones entre procesos. Por ejemplo, el proceso A puede crear un evento con nombre y establecerlo así: SetEvent(hFileIsReady). Después, sea cual sea el número de procesos que esperan que se establezca este evento (llamando al WaitForSingleObject(hFileIsReady)), continuarán inmediatamente su ejecución normal hasta que el evento vuelva a restablecerse dentro del proceso A por ResetEvent(hFileIsReady).

Pero la combinación mutex/condition/bool no puede permitirse tal funcionalidad. Por supuesto, podemos usar boost named_condition y named_mutex. Sin embargo, ¿qué pasa con la variable booleana que debemos verificar antes de esperar?

8

Se podría utilizar una promesa y un futuro, a partir de hilos impulso:

#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

Para cualquier persona involucrada en o trabajando en portar multi-hilo de código nativo de Windows C/C++ para Linux/Mac, we've authored an open source (MIT-licensed) library que implementa tanto manual y reinicio automático de eventos WIN32 encima de pthreads, incluida una implementación completa de WaitForSingleObject y WaitForMultipleObjects, lo que lo convierte en el único puerto WFMO que conozco disponible en Linux/Mac.

pevents is available on GitHub y ha sido bastante probado en la batalla y está siendo utilizado por algunos grandes nombres; también hay un puerto de impulso de pevents flotando por ahí.

El uso de pevents hará que el código de portado de Windows sea mucho más fácil ya que los paradigmas subyacentes son radicalmente diferentes entre Windows y las plataformas posix, aunque recomendaría a cualquiera que escriba código multiplataforma que use una biblioteca multiproceso multiproceso existente como boost en El primer lugar.

+0

¡Gracias! He estado buscando una solución WFMO portátil por años. Es una pena que el estándar de C++ no tenga nada parecido, AFAIK, boost tampoco. Definitivamente tengo un uso inmediato para tu biblioteca. –

Cuestiones relacionadas