2012-10-02 10 views
8

Ho un due fili, uno che funziona in un ciclo stretto, e l'altro che ogni tanto ha bisogno di eseguire una sincronizzazione con la prima:pthreads: filo inedia causata da rapido ri-bloccaggio

// thread 1 
while(1) 
{ 
    lock(work); 
    // perform work 
    unlock(work); 
} 

// thread 2 
while(1) 
{ 
    // unrelated work that takes a while 
    lock(work); 
    // synchronizing step 
    unlock(work); 
} 

mia intenzione è che il thread 2 può, prendendo il blocco, sospendere efficacemente il thread 1 ed eseguire la sincronizzazione necessaria. Thread 1 può anche offrire di mettere in pausa, sbloccando, e se il thread 2 non è in attesa sul blocco, re-lock e torna al lavoro.

Il problema che ho riscontrato è che i mutex non sono corretti, quindi il thread 1 blocca rapidamente il mutex e affama il thread 2. Ho tentato di utilizzare pthread_yield e finora sembra funzionare correttamente, ma non lo sono sicuro che funzionerà per tutti i sistemi/numero di core. C'è un modo per garantire che thread 1 sarà sempre il thread 2, anche su sistemi multi-core?

Qual è il modo più efficace per gestire questo processo di sincronizzazione?

+0

Quello che hai descritto nella sua domanda non ha alcuna relazione con l'equità, se si vuole Thread 2 di essere con priorità superiore rispetto al filo 1, uno dei modi per realizzarlo, utilizzando le variabili di condizione. – Michael

+0

Questa domanda è molto simile a [* Sezione critica (Linux) *] (http://stackoverflow.com/q/6449732/134633). – caf

risposta

3

È possibile costruire un "blocco biglietto" FIFO in cima pthreads mutex, in queste righe:

#include <pthread.h> 

typedef struct ticket_lock { 
    pthread_cond_t cond; 
    pthread_mutex_t mutex; 
    unsigned long queue_head, queue_tail; 
} ticket_lock_t; 

#define TICKET_LOCK_INITIALIZER { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER } 

void ticket_lock(ticket_lock_t *ticket) 
{ 
    unsigned long queue_me; 

    pthread_mutex_lock(&ticket->mutex); 
    queue_me = ticket->queue_tail++; 
    while (queue_me != ticket->queue_head) 
    { 
     pthread_cond_wait(&ticket->cond, &ticket->mutex); 
    } 
    pthread_mutex_unlock(&ticket->mutex); 
} 

void ticket_unlock(ticket_lock_t *ticket) 
{ 
    pthread_mutex_lock(&ticket->mutex); 
    ticket->queue_head++; 
    pthread_cond_broadcast(&ticket->cond); 
    pthread_mutex_unlock(&ticket->mutex); 
} 

Sotto questo tipo di regime, non pthreads di basso livello mutex si svolge mentre un thread è all'interno della sezione critica protetta da ticketlock, che consente ad altri thread di unirsi alla coda.

5

Nel tuo caso è meglio usare condition variable per notificare il secondo thread quando è necessario per svegliare ed eseguire tutte le operazioni richieste.

+1

Si prega di citare anche i semafori come una potenziale soluzione a problemi come questi, sono spesso trascurati! –

+0

Qualche esempio o snippet su come i semafori possono essere usati per questo problema? – Atanu

+0

[Questa mia risposta] (http://stackoverflow.com/a/6453925/134633) potrebbe essere utile, dove mostro come usare le variabili di condizione pthread per implementare un blocco ticket first-in-first-out. – caf

2

pthread offre una nozione di priorità thread nella sua API. Quando due thread sono in competizione su un mutex, il criterio di pianificazione determina quale verrà ottenuto. La funzione pthread_attr_setschedpolicy consente di impostarla e pthread_attr_getschedpolicy consente di recuperare le informazioni.

Ora la cattiva notizia:

  • quando solo due thread sono bloccare/sbloccare un mutex, non riesco a vedere alcun tipo di concorso, il primo che gestisce l'istruzione atomica prende, gli altri blocchi. Non sono sicuro se questo attributo si applica qui.
  • La funzione può assumere diversi parametri (SCHED_FIFO, SCHED_RR, SCHED_OTHER e SCHED_SPORADIC), ma in this question, è stato risposto che solo SCHED_OTHER è stato sostenuto su linux)

Quindi darei un colpo se fossi tu, ma non aspettarti troppo. pthread_yield sembra più promettente per me. Maggiori informazioni disponibili here.

0

Il blocco del biglietto sopra sembra il migliore. Tuttavia, per assicurare che il tuo pthread_yield funzioni, potresti avere un bool in attesa, che viene impostato e ripristinato da thread2. thread1 produce finché viene impostato l'attesa bool.

0

Ecco una soluzione semplice che funzionerà per il tuo caso (due thread). Se si utilizza std::mutex, questa classe è una sostituzione drop-in. Cambia il tuo mutex in questo tipo e sei sicuro che se un thread tiene il lock e l'altro è in attesa su di esso, una volta che il primo thread si sblocca, il secondo thread prenderà il lock prima che il primo thread possa bloccarlo di nuovo.

Se più di due thread utilizzano simultaneamente il mutex, esso continuerà a funzionare ma non ci sono garanzie di equità.

Se si utilizza semplicemente pthread_mutex_t, è possibile modificare facilmente il codice di blocco in base a questo esempio (lo sblocco rimane invariato).

#include <mutex> 

// Behaves the same as std::mutex but guarantees fairness as long as 
// up to two threads are using (holding/waiting on) it. 
// When one thread unlocks the mutex while another is waiting on it, 
// the other is guaranteed to run before the first thread can lock it again. 

class FairDualMutex : public std::mutex { 
public: 
    void lock() { 
     _fairness_mutex.lock(); 
     std::mutex::lock(); 
     _fairness_mutex.unlock(); 
    } 
private: 
    std::mutex _fairness_mutex; 
};