2016-03-19 8 views
0

Ich bin auf der Suche nach einem sehr einfachen Beispiel, um einen Deadlock mit pthread_join zu demonstrieren; Dies ist jedoch nicht trivial.Einfaches Beispiel für pthread_join Deadlock

ich damit begonnen:

void* joinit(void* tid) 
{ 
    pthread_t* tid_c = (pthread_t*)tid; 
    int retval = pthread_join(*tid_c, NULL); 
    printf("In joinit: tid = %d, retval = %d \n", *tid_c, retval); 
    return NULL; 
} 

int main() 
{ 
    pthread_t thread1; 
    pthread_t thread2; 

    pthread_create(&thread1, NULL, joinit, &thread2); 
    pthread_create(&thread2, NULL, joinit, &thread1); 

    pthread_join(thread2, NULL); 

    return 0; 
} 

Aber es jedoch sagt: 'EINVAL' (ungültiges Argument), weil thread2 noch nicht, wenn pthread_create für thread1 angegeben wird aufgerufen wird.

Irgendwelche Ideen?

+0

habe ich versucht, diese und es funktioniert, aber das Problem ist, es funktioniert nur manchmal, weil ich manchmal EIVAL wieder zu bekommen. ICH überprüfe errno in der Methode joinit! –

+0

Ja, Sie können nicht entscheiden, wann ein Thread gestartet wird, das OS tut das. –

+0

Ja, ich weiß. Ich muss eine zuverlässige Deadlock-Situation haben. –

Antwort

2

Wenn Sie nur demonstrieren wollen, dass ein pthread_join eine Sackgasse führen kann, Sie etwas ähnliches wie dem folgenden Code tun könnte:

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

void* joinit(void* tid) 
{ 
    printf("In %#x, waiting on %#x\n", pthread_self(), (*((pthread_t*)tid))); 
    pthread_join((*((pthread_t*)tid)), NULL); 
    printf("Leaving %#x\n", pthread_self()); 
    return NULL; 
} 

int main(void) 
{ 
    pthread_t thread1 = pthread_self(); 
    pthread_t thread2; 
    pthread_create(&thread2, NULL, joinit, &thread1); 
    joinit(&thread2); 
    return 0; 
} 

Dies wird die Haupt-Thread verursachen auf der erzeugte Thread zu warten und Der generierte Thread, der auf den Hauptthread wartet (was zu einem garantierten Deadlock führt), ohne dass zusätzliche Sperr-Primitive erforderlich sind, um das, was Sie demonstrieren möchten, zu überladen.

Und einige Ihrer Fragen mehr direkt zu beantworten:

es sagt: ‚EINVAL‘ (ungültiges Argument), weil thread2 noch nicht, wenn pthread_create für thread1 angegeben wird aufgerufen wird.

... und von einem Ihrer Kommentare ...

habe ich versucht, diese und es funktioniert, aber das Problem ist, es funktioniert nur manchmal manchmal, weil ich EINVAL wieder bekommen.

In Ihrem Code, rufen Sie pthread_create nacheinander die zwei Threads zu erstellen:

pthread_create(&thread1, NULL, joinit, &thread2); 
pthread_create(&thread2, NULL, joinit, &thread1); 

In Ihrem joinit Code, greifen Sie den Thread-Handle übergeben verbinden auf:

pthread_t* tid_c = (pthread_t*)tid; 
int retval = pthread_join(*tid_c, NULL); 

Der Grund dafür manchmal funktioniert und andere, die Sie erhalten EINVAL hat mit time slices zu jedem Threa zugeordnet zu tun d's Kontext und sequencing. Wenn der erste pthread_create aufgerufen wird, haben Sie nach der Rückgabe ein gültiges Handle zu aber der Handle zu thread2 ist noch nicht gültig, zumindest nicht bis der 2. pthread_create aufgerufen wird.

Wenn ein Thread erstellt wird, kann die Handlung des Threads, der "lebendig" wird (d. H. Die tatsächlich ausgeführte Thread-Funktion), zusätzliche Zeit beanspruchen, obwohl das zurückgegebene Thread-Handle gültig ist. In diesen Fällen besteht die Möglichkeit, dass ein Thread mehr Code ausführen kann als erwartet.In Ihrem Code, beide pthread_create Funktionen könnte zufällig in der Zeitscheibe genannt wurden für den Haupt-Thread zugeordnet, die jeweils genug „Zeit“, bevor man die pthread_join Anweisung ermöglicht tid_c auf einen gültigen Griff Punkt erzeugte Thread geben könnte; Im EINVAL Fall wurde pthread_create(&thread1, NULL, joinit, &thread2) aufgerufen und der erzeugte Thread traf die pthread_join(*tid_c, NULL), bevorpthread_create(&thread2, NULL, joinit, &thread1)thread2 ein gültiges Handle geben konnte (verursacht den Fehler).

Wenn Sie Ihren Code ähnlich halten wollte, wie es jetzt ist, würden Sie brauchen eine Sperre von einer Art hinzufügen, um die Fäden zu gewährleisten, nicht oder rufen Sie vorzeitig alles verlassen:

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

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 

void* joinit(void* tid) 
{ 
    /* this can be above the lock because it will be valid after lock is acquired */ 
    pthread_t* tid_c = (pthread_t*)tid; 
    int retval = -1; 
    pthread_mutex_lock(&lock); 
    pthread_mutex_unlock(&lock); 
    printf("%#x waiting on %#x\n", pthread_self(), *tid_c); 
    retval = pthread_join(*tid_c, NULL); 
    printf("In joinit: tid = %d, retval = %d \n", *tid_c, retval); 
    return NULL; 
} 

int main() 
{ 
    pthread_t thread1; 
    pthread_t thread2; 
    /* get the lock in the main thread FIRST */ 
    pthread_mutex_lock(&lock); 
    pthread_create(&thread1, NULL, joinit, &thread2); 
    pthread_create(&thread2, NULL, joinit, &thread1); 
    /* by this point, both handles are "joinable", so unlock */ 
    pthread_mutex_unlock(&lock); 

    /* can wait on either thread, but must wait on one so main thread doesn't exit */ 
    pthread_join(thread2, NULL); 
    return 0; 
} 

Hope this kann helfen.

+0

ist erwähnenswert, dass das Ausführen von nur Ihrem Code nicht OP bewusst machen, wenn Deadlock passiert ist. Sie sollten das Rückgabewert von pthread_join dafür überprüfen. In diesem Fall wird pthread_join 35 (EDEADLK) zurückgeben, und jeder Thread wird einfach zurückkehren. 'int ret = Pthread_join ((* ((pthread_t *) tid)), NULL); printf ("% d% s \ n", ret, strerror (RET)); ' output: In 0x7fb4740, Warten auf 0x77ca700 In 0x77ca700 auf 0x7fb4740 35 Ressourcen Deadlock Warte vermieden Weggehen 0x77ca700 0 Erfolg Verlassen 0x7fb4740 Im Wesentlichen hier behandelt pthread_join Deadlock für uns und übersprungen warten Teil. – kalimba

+0

@kalimba .. tatsächlich läuft nur mein Code verursacht einen Deadlock auf einigen Systemen (wie Windows und OpenBSD); andere es _might_ gibt einen Wert zurück, der _might_ anzeigen könnte, dass eine Deadlock-Bedingung aufgetreten ist (z. B. eine Kerneleinstellung). Auf meinem Slackware-System ist 'EDEADLK' als' 22' definiert (nicht '35').POSIX ist ein loser Standard. Während es wichtig ist, den Rückgabewert von 'pthread_join' zu überprüfen, ist es viel wichtiger, zu verstehen, was ein toter Lock ist, und ihn überhaupt zu vermeiden (da der OP dies demonstrieren wollte) vermeide einen solchen Fall in erster Linie). – txtechhelp

+0

schätze deine Antwort :) Meine Informationen waren nur auf Posix beschränkt. – kalimba

0

Der Hauptgrund für Ihren Fehler ist, dass Sie zwei Threads haben, die jeweils darauf warten, dass der gleiche Thread terminiert wird, weil pthread_join in main aufgerufen wird. Ein weiteres Problem besteht darin, dass Sie nicht sicherstellen, dass jeder Thread die ID des anderen Threads korrekt erkennt.

Fix es wie folgt aus:

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

pthread_t thread1; 
pthread_t thread2; 
pthread_mutex_t mutex; 
pthread_cond_t cond; 
int go = 0; 


void* joinit(void* ptr) 
{ 
    // wait until both thread IDs are known 
    pthread_mutex_lock(&mutex); 
    while (go == 0) 
     pthread_cond_wait(&cond, &mutex); 
    pthread_mutex_unlock(&mutex); 

    pthread_t* tid_c = *((pthread_t**) ptr); 
    printf("About to wait\n"); 
    int retval = pthread_join(*tid_c, NULL); 
    printf("In joinit: tid = %d, retval = %d \n", *tid_c, retval); 

    // tell the other threads we're done 
    pthread_mutex_lock(&mutex); 
    go++; 
    pthread_cond_broadcast(&cond); 
    pthread_mutex_unlock(&mutex); 

    return NULL; 
} 

int main() 
{ 
    // setup synchronization 
    pthread_mutex_init(&mutex, NULL); 
    pthread_cond_init(&cond, NULL); 

    pthread_create(&thread1, NULL, joinit, &thread2); 
    pthread_create(&thread2, NULL, joinit, &thread1); 

    // tell the threads to go 
    pthread_mutex_lock(&mutex); 
    go = 1; 
    pthread_cond_broadcast(&cond); 
    pthread_mutex_unlock(&mutex); 

    // wait for both threads to finish 
    pthread_mutex_lock(&mutex); 
    while (go != 3) 
     pthread_cond_wait(&cond, &mutex); 
    pthread_mutex_unlock(&mutex); 

    return 0; 
}