2012-06-12 19 views
5

Per comprendere il codice delle variabili di condizione pthread, ho scritto la mia versione. Sembra corretto? Lo sto usando in un programma, funziona, ma funziona sorprendentemente molto più velocemente. Originariamente il programma impiega circa 2,5 secondi e con la mia versione di variabili di condizione ci vogliono solo 0,8 secondi e anche l'output del programma è corretto. Tuttavia, non sono sicuro, se la mia implementazione è corretta.Implementazione delle variabili di condizione

struct cond_node_t 
{ 
    sem_t s; 
    cond_node_t * next; 
}; 

struct cond_t 
{ 
    cond_node_t * q;    // Linked List 
    pthread_mutex_t qm;     // Lock for the Linked List 
}; 

int my_pthread_cond_init(cond_t * cond) 
{ 
    cond->q = NULL; 
    pthread_mutex_init(&(cond->qm), NULL); 
} 

int my_pthread_cond_wait(cond_t* cond, pthread_mutex_t* mutex) 
{ 
    cond_node_t * self; 

    pthread_mutex_lock(&(cond->qm)); 
    self = (cond_node_t*)calloc(1, sizeof(cond_node_t)); 
    self->next = cond->q; 
    cond->q = self; 
    sem_init(&self->s, 0, 0); 
    pthread_mutex_unlock(&(cond->qm)); 

    pthread_mutex_unlock(mutex); 
    sem_wait(&self->s); 
    free(self); // Free the node 
    pthread_mutex_lock(mutex); 
} 

int my_pthread_cond_signal(cond_t * cond) 
{ 
    pthread_mutex_lock(&(cond->qm)); 
    if (cond->q != NULL) 
    { 
     sem_post(&(cond->q->s)); 
     cond->q = cond->q->next; 
    } 
    pthread_mutex_unlock(&(cond->qm)); 
} 

int my_pthread_cond_broadcast(cond_t * cond) 
{ 
    pthread_mutex_lock(&(cond->qm)); 
    while (cond->q != NULL) 
    { 
     sem_post(&(cond->q->s)); 
     cond->q = cond->q->next; 
    } 
    pthread_mutex_unlock(&(cond->qm)); 
} 
+0

Stai liberando il nodo 'self' senza rimuoverlo dalla lista. –

+0

@ n.m. Il nodo 'self' viene rimosso da' signal' e 'broadcast'. –

+0

@JensGustedt sì, mio ​​male –

risposta

1

Fondamentalmente la vostra strategia sembra ok, ma è necessario un pericolo maggiore, qualche comportamento non definito, e un pick nit:

  • vostro non stanno controllando i valori di ritorno delle vostre funzioni POSIX. In particolare, sem_wait è interrompibile, quindi in condizioni di carico elevato o sfortuna il thread verrà risvegliato in modo spurio. Dovresti catturare con attenzione tutto ciò che
  • nessuna delle tue funzioni restituisce un valore. Se un utente delle funzioni deciderà di utilizzare i valori restituiti un giorno, si tratta di un comportamento non definito. Analizzare attentamente i codici di errore che le funzioni di condizione possono restituire e fare proprio questo.
  • non gettato il ritorno di malloc o calloc

Edit: In realtà, non hanno bisogno malloc/free a tutti. Anche una variabile locale farebbe lo stesso.

+0

Perché non eseguire il ritorno di malloc/calloc? – pythonic

+1

Innanzitutto, la cosa più importante è che è inutile. È valido assegnare 'void *' a qualsiasi puntatore, questo è ciò per cui è fatto in C. Second, usa solo cast se sono assolutamente necessari. Sono difficili da trovare testualmente e disattivano tutti gli avvisi e la diagnostica. Terzo, questo può nascondere un bug sottile, quando dimentichi il '# include 'e il compilatore prende questo per restituire' int'. –

2

Lei non sembra rispettare questo requisito:

Queste funzioni atomicamente rilasciare mutex e causare il thread chiamante per bloccare sulla condizione cond variabili; atomicamente qui significa "atomicamente rispetto all'accesso da un altro thread al mutex e quindi alla variabile di condizione". Cioè, se un altro thread è in grado di acquisire il mutex dopo che il thread about-to-block lo ha rilasciato, una successiva chiamata a pthread_cond_broadcast() o pthread_cond_signal() in quel thread deve comportarsi come se fosse emessa dopo il thread about-to-block è bloccato.

Si sblocca e quindi si attende. Un altro thread può fare molte cose tra queste operazioni.

P.S. Non sono sicuro che se interpreto correttamente questo paragrafo, sentiti libero di indicare il mio errore.

+0

Non vedo che questo è un problema qui. Il semaforo è inizializzato correttamente. Quindi, anche se un altro thread viene attivato, il semaforo memorizzerà qualsiasi token che verrà "post" con "segnale" o "trasmissione". Tale operazione 'post' può avvenire prima che il thread invochi effettivamente' sem_wait' o mentre è già in. In entrambi i casi il thread continuerà l'esecuzione. –

4

A parte i controlli di valore di ritorno mancanti, ci sono alcuni più problemi che dovrebbero essere risolvibile:

  • sem_destroy non viene chiamato.
  • Segnale/trasmissione toccano lo cond_node_t dopo aver risvegliato il thread di destinazione, con conseguente conseguente utilizzo after-free.

Ulteriori commenti:

  • L'omesso distruggono operazione può richiedere modifiche alle altre operazioni in modo che sia sicuro per distruggere la variabile di condizione quando POSIX dice che deve essere al sicuro. Non sostenere distruggere o imporre restrizioni più forti su quando può essere chiamato semplificherà le cose.
  • Un'implementazione di produzione gestiva la cancellazione del thread.
  • Il backup di un'attesa (come richiesto per la cancellazione del thread e dei timeout pthread_cond_timedwait) può causare complicazioni.
  • L'implementazione attacca i thread in userland, che viene eseguito in alcune implementazioni di produzione per motivi di prestazioni; Non capisco esattamente perché.
  • L'implementazione mette sempre in coda i thread nell'ordine LIFO. Questo è spesso più veloce (come a causa degli effetti della cache) ma può portare alla fame. L'implementazione della produzione può utilizzare l'ordine FIFO a volte per evitare la fame.
Problemi correlati