2012-06-12 6 views
5

Um den Code der PThread-Zustandsvariablen zu verstehen, habe ich meine eigene Version geschrieben. Sieht es richtig aus? Ich benutze es in einem Programm, es funktioniert, aber es funktioniert überraschend viel schneller. Ursprünglich dauert das Programm etwa 2,5 Sekunden und mit meiner Version der Zustandsvariablen dauert es nur 0,8 Sekunden, und die Ausgabe des Programms ist auch korrekt. Ich bin mir jedoch nicht sicher, ob meine Implementierung korrekt ist.Implementierung der Zustandsvariablen

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

Sie befreien den Knoten "self", ohne ihn aus der Liste zu entfernen. –

+0

@ n.m. Der "self" -Knoten wird durch "signal" und "broadcast" entfernt. –

+0

@JensGustedt ja, mein Schlechter –

Antwort

1

Grundsätzlich Ihre Strategie sieht ok, aber Sie haben eine große Gefahr, etwas undefiniertes Verhalten, und eine nit Pick:

  • Sie nicht die Rückgabewerte Ihrer POSIX-Funktionen inspizieren. Insbesondere ist sem_wait unterbrechbar, so dass Ihr Thread bei starker Belastung oder Pech fälschlicherweise geweckt wird. Sie müssen alle diese
  • sorgfältig abfangen, keine Ihrer Funktionen gibt einen Wert zurück. Wenn ein Benutzer der Funktionen eines Tages entscheidet, die Rückgabewerte zu verwenden, ist dies ein undefiniertes Verhalten. Analysieren Sie sorgfältig die Fehlercodes, die die Bedingungsfunktionen zurückgeben dürfen, und tun Sie dies genau.
  • werfen nicht die Rückkehr von malloc oder calloc

Edit: Eigentlich Sie malloc/free überhaupt nicht brauchen. Eine lokale Variable würde auch funktionieren.

+0

Warum werfen Sie nicht die Rückgabe von malloc/calloc? – pythonic

+1

Erstens, am wichtigsten ist es nutzlos. Es ist zulässig, jedem Zeiger 'void *' zuzuweisen, dafür ist es in C gemacht. Zweitens, verwende nur Umwandlungen, wenn sie absolut notwendig sind. Sie sind schwer zu finden und schalten alle Warnungen und Diagnosen aus. Drittens kann dies einen subtilen Fehler verbergen, wenn Sie das '# include' vergessen und der Compiler dies übernimmt, um' int' zurückzugeben. –

2

Sie scheinen diese Anforderung nicht zu respektieren:

Diese Funktionen atomar Mutex freizugeben und den aufrufenden Thread verursachen auf der Zustandsgröße cond zu blockieren; atomar bedeutet hier "atomar in Bezug auf den Zugriff eines anderen Threads auf den Mutex und dann die Zustandsvariable". Das heißt, wenn ein anderer Thread in der Lage ist, den Mutex zu erhalten, nachdem der "Too-to-Block" -Thread ihn freigegeben hat, dann soll sich ein nachfolgender Aufruf von pthread_cond_broadcast() oder pthread_cond_signal() in diesem Thread so verhalten, als ob er nach dem Der Thread "Von-zu-Block" wurde blockiert.

Sie entsperren und warten dann. Ein anderer Thread kann viele Dinge zwischen diesen Operationen ausführen.

P.S. Ich bin mir selbst nicht sicher, ob ich diesen Absatz richtig interpretiere, bitte geben Sie mir meinen Fehler an.

+0

Ich sehe nicht, dass das hier ein Problem ist. Der Semaphor ist korrekt initialisiert. Selbst wenn also ein anderer Thread einsetzt, wird der Semaphor jedes Token speichern, das durch ein "Signal" oder "Broadcast" nachgerüstet wird. Eine solche "post" -Operation kann passieren, bevor der Thread tatsächlich 'sem_wait' aufruft oder während er bereits aktiv ist. In beiden Fällen wird der Thread die Ausführung fortsetzen. –

4

Neben den fehlenden Rückgabewert überprüft, gibt es einige weitere Probleme, die fixierbar sein sollte:

  • sem_destroy wird nicht aufgerufen.
  • Signal/Broadcast berühren Sie die cond_node_t nach dem Aufwecken der Ziel-Thread, was möglicherweise zu einer Verwendung-nach-Frei.

Weitere Anmerkungen:

  • Die versäumte zerstören Betrieb Änderungen an den anderen Operationen erfordern, so dass es sicher ist, die Zustandsgröße zu zerstören, wenn POSIX sagt es sicher sein wird. Nicht zu unterstützen zerstören oder auferlegen stärkere Einschränkungen, wenn es aufgerufen werden kann, wird die Dinge vereinfachen.
  • Eine Produktionsimplementierung würde Thread-Löschung behandeln.
  • Das Zurückstellen einer Wartezeit (wie zum Beispiel für das Löschen von Threads und pthread_cond_timedwait Timeouts) kann zu Komplikationen führen.
  • Ihre Implementierung führt Threads in userland aus, was in einigen Produktionsimplementierungen aus Leistungsgründen erfolgt. Ich verstehe nicht genau warum.
  • Ihre Implementierung reiht Threads immer in LIFO-Reihenfolge ein. Dies ist oft schneller (z. B. aufgrund von Cache-Effekten), kann jedoch zu einem Mangel führen. Die Produktionsimplementierung kann die FIFO-Reihenfolge manchmal verwenden, um ein Verhungern zu vermeiden.
Verwandte Themen