2011-11-14 9 views

risposta

11

Un AutoResetEvent è più simile a un semaforo binario. Le persone che dicono "variabili condizionali" non sono sbagliate di per sé, ma le variabili di condizione sono usate in situazioni simili, piuttosto che essere oggetti simili. È possibile implementare un AutoResetEvent (senza nome) sulla parte superiore del variabili di condizione:

#include <pthread.h> 
#include <stdio.h> 

class AutoResetEvent 
{ 
    public: 
    explicit AutoResetEvent(bool initial = false); 

    ~AutoResetEvent(); 
    void Set(); 
    void Reset(); 

    bool WaitOne(); 

    private: 
    AutoResetEvent(const AutoResetEvent&); 
    AutoResetEvent& operator=(const AutoResetEvent&); // non-copyable 
    bool flag_; 
    pthread_mutex_t protect_; 
    pthread_cond_t signal_; 
}; 

AutoResetEvent::AutoResetEvent(bool initial) 
: flag_(initial) 
{ 
    pthread_mutex_init(&protect_, NULL); 
    pthread_cond_init(&signal_, NULL); 
} 

void AutoResetEvent::Set() 
{ 
    pthread_mutex_lock(&protect_); 
    flag_ = true; 
    pthread_mutex_unlock(&protect_); 
    pthread_cond_signal(&signal_); 
} 

void AutoResetEvent::Reset() 
{ 
    pthread_mutex_lock(&protect_); 
    flag_ = false; 
    pthread_mutex_unlock(&protect_); 
} 

bool AutoResetEvent::WaitOne() 
{ 
    pthread_mutex_lock(&protect_); 
    while(!flag_) // prevent spurious wakeups from doing harm 
    pthread_cond_wait(&signal_, &protect_); 
    flag_ = false; // waiting resets the flag 
    pthread_mutex_unlock(&protect_); 
    return true; 
} 

AutoResetEvent::~AutoResetEvent() 
{ 
    pthread_mutex_destroy(&protect_); 
    pthread_cond_destroy(&signal_); 
} 


AutoResetEvent event; 

void *otherthread(void *) 
{ 
    event.WaitOne(); 
    printf("Hello from other thread!\n"); 
    return NULL; 
} 


int main() 
{ 
    pthread_t h; 
    pthread_create(&h, NULL, &otherthread, NULL); 
    printf("Hello from the first thread\n"); 
    event.Set(); 

    pthread_join(h, NULL); 
    return 0; 
} 

Se, tuttavia, è necessario nominato eventi di ripristino automatico, è probabile che vuole guardare i semafori, e può avere un momento leggermente più difficile traduzione il tuo codice. Ad ogni modo guarderei con attenzione alla documentazione per i pthread sulla tua piattaforma, le variabili di condizione e gli eventi di reset automatico non sono gli stessi e non si comportano allo stesso modo.

0

Beh, le probabilità sono più come un mutex - hai un numero di chiamanti che vanno per una risorsa condivisa, ma solo una è consentita. Nel caso mutex, i chiamanti proverebbero ad ottenere il mutex (es. Phtread_mutex_lock) , fai le loro cose, quindi rilascia (pthread_mutex_unlock) in modo che un altro chiamante possa entrare.

+1

No, non assomiglia affatto a un mutex. È più come una variabile di condizione. – Gabe

3

Sono sicuro che stai cercando le variabili di condizione. La risposta accettata a questa altra domanda SO: Condition variables in C# - sembra confermarlo.

Vedere ad es. this tutorial per dettagli sulle variabili di condizione nei thread POSIX.

+1

A Microsoft piace confondere la distinzione tra "librerie", "piattaforme" e "lingue" in un'unica grande confusione che ti costringe a "comprare Microsoft". In effetti, la classe "AutoResetEvent" in .Net è un wrapper attorno alla primitiva "Event" in Win32 e analoga a "condition variables" in PThreads. Scopri PThreads per Linux. PThreads può essere utilizzato su altre piattaforme oltre a Linux e con altri linguaggi (C, Python, ecc. Ecc.) Oltre al C++. – paulsm4

+0

Basta chiedersi, sarebbe più semplice implementarlo usando boost: libreria dei thread? – derekhh

+0

@derekhh: Non so "più facile", le variabili di condizione sono un costrutto abbastanza semplice, ma se stai già usando boost :: thread (che IMO è una scelta perfetta), usa con tutti i termini la sua variabile di condizione wrapper, si adatta perfettamente con il resto del codice: http://www.boost.org/doc/libs/1_47_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref –

2

È possibile riorganizzare facilmente gli oggetti evento API Win32 utilizzando mutex POSIX e variabili di condizione.

Tuttavia alcuni dei commenti sopra mi fa affermare questo:

Una variabile di condizione è non analoga ad un oggetto Event. Una variabile di condizione è fondamentalmente diversa da un Evento in quanto non ha memoria o stato, nel senso che se non c'è nessuno bloccato alla variabile di condizione nel momento in cui si chiama pthread_cond_signal o pthread_cond_broadcast non accadrà nulla, in particolare se un thread viene più tardi per bloccare tramite pthread_cond_wait il blocco sarà.

I'l tentare di delineare un'implementazione rapida evento auto-reset:

class event 
{ 
public: 
    event(): signalled_ (false) {} 

    void signal() 
    { 
    std::unique_lock<std::mutex> lock(mutex_); 
    signalled_ = true; 
    cond_.notify_one(); 
    } 

    void wait() 
    { 
    std::unique_lock<std::mutex> lock(mutex_); 

    while (!signalled_) 
     cond_.wait (lock); 
    signalled_ = false; 
    } 

protected: 
    std::mutex mutex_; 
    std::condition_variable cond_; 
    bool signalled_; 
}; 
1

Le variabili condizionali sono NON l'equivalente di AutoResetEvent. Sono l'equivalente dei monitor. La differenza è fondamentale e potrebbe causare deadlock se non utilizzata correttamente:

Immaginare due thread A e B in un programma C#. A chiama WaitOne() e B chiama Set(). Se B esegue Set() prima che A raggiunga la chiamata a WaitOne(), non c'è alcun problema perché il segnale inviato a AutoResetEvent() da Set() è persistente e rimarrà impostato fino a quando non viene eseguito WaitOne().

Ora in C, immagina due thread C e D. C chiama wait(), D chiama notify(). Se C sta aspettando già quando D chiama notify() tutto è ok. Se C non riesce a raggiungere l'attesa() prima che D invii notifiche(), si ha un deadlock perché il segnale viene perso se nessuno lo sta aspettando e lo stato della variabile condizionale è ancora "non impostato".

State molto attenti a questo.

1

L'esempio dalla documentazione Discussione/Condizione di Boost è abbastanza simile al ManualResetEvent normale e l'uso AutoResetEvent: http://www.boost.org/doc/libs/1_53_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref
(ho fatto alcune piccole modifiche per chiarezza)

boost::condition_variable cond; 
boost::mutex mut; 
bool data_ready; 

void wait_for_data_to_process() 
{ 
    boost::unique_lock<boost::mutex> lock(mut); 
    while(!data_ready) 
    { 
     cond.wait(lock); 
    } 
} 

void prepare_data_for_processing() 
{ 
    { //scope for lock_guard 
     boost::lock_guard<boost::mutex> lock(mut); 
     data_ready=true; 
    } 
    cond.notify_one(); 
} 

Si noti che le condizioni forniscono il attendere/notificare il meccanismo di AutoResetEvent e ManualResetEvent ma richiede il funzionamento di un mutex.