Producer-Cosumer-Problem Deadlock erzwingen Theoriefrage

#1

Hallo,
mit pthread.h und in einfaches C habe ich ein einfaches PCP-Beispiel. Läuft so weit auch ganz gut, bis auf das Beenden und manchmal gibt es keinerlei Ausgabe.
Jetzt wollte ich fragen, ob es theoretisch möglich ist, Deadlock zu erzwingen.
Meine Annahme: Nein, da dafür Thread 1 Ressource A und Thread 2 Ressource B benötigen würde (minimal), aber beide gesperrt sind. Bei PCP gibt es aber nur eine Ressource, und sobald P oder C dran ist kann sie wieder den richtigen Zustand annehmen.
Also wie ist das in C möglich? Am besten wäre ein einfaches Beispiel mit 2 Threads, Condition, Mutex, wait, pthread usw.

#2

Das Beispiel-Programm sieht so aus, ich verstehe nicht, wieso gestartet wird und:
a) nix passiert (keine Ausgabe) und nicht beendet wird, oder
b) alles ausgegeben wird und nicht beendet wird?:

#include <pthread.h>

#define MAX 10			/* Numbers to produce */
pthread_mutex_t the_mutex;
pthread_cond_t condc, condp;
int buffer = 0;

void * producer(void * ptr) {
    int i;
    for (i = 1; i <= MAX; i++) {
        pthread_mutex_lock(&the_mutex);
        while (buffer != 0) {
            pthread_cond_wait(&condp, &the_mutex);
        }
    
        printf("%d erzeugt
", i);
        buffer = i;
    
        pthread_cond_signal(&condc);
        pthread_mutex_unlock(&the_mutex);
    }
    pthread_exit(0);
}

void * consumer(void * ptr) {
    int i;
    for (i = 1; i <= MAX; i++) {
        pthread_mutex_lock(&the_mutex);
        while (buffer == 0) {
            pthread_cond_wait(&condc, &the_mutex);
        }
    
        printf("%d verbraucht
", buffer);
        buffer = 0;
    
        pthread_cond_signal(&condp);
        pthread_mutex_unlock(&the_mutex);
    }
    pthread_exit(0);
}

int main(int argc, char ** argv) {
    pthread_t pro, con;
    
    pthread_mutex_init(&the_mutex, NULL);
    pthread_cond_init(&condc, NULL);
    pthread_cond_init(&condp, NULL);
    
    pthread_create(&con, NULL, consumer, NULL);
    pthread_create(&pro, NULL, producer, NULL);
    
    pthread_join(&con, NULL);
    pthread_join(&pro, NULL);
    
    pthread_mutex_destroy(&the_mutex);	/* Free up the_mutex */
    pthread_cond_destroy(&condc);		/* Free up consumer condition variable */
    pthread_cond_destroy(&condp);		/* Free up producer condition variable */
    
    return 0;
}

Das ist Ansi-C.

Kann bitte i-jemand etwas erklären?

*** Edit ***

#include <Pthread.h>

#define MAX 1000			/* Numbers to produce */
pthread_mutex_t mutexA;
pthread_mutex_t mutexB;
pthread_mutex_t the_mutex;
pthread_cond_t condc, condp;
int buffer = 0;

void * producer(void * ptr) {
    /*
    int i;
    for (i = 1; i <= MAX; i++) {
        pthread_mutex_lock(&the_mutex);
        while (buffer != 0) {
            pthread_cond_wait(&condp, &the_mutex);
        }
    
        printf("%d erzeugt
", i);
        buffer = i;
    
        pthread_cond_signal(&condc);
        pthread_mutex_unlock(&the_mutex);
    }
    pthread_exit(0);
    */
    int i;
    for (i = 1; i <= MAX; i++) {
	pthread_mutex_lock(&mutexA);  
	/* printf("Es droht Gefahr..
"); // Forcierung */
	pthread_mutex_lock(&mutexB);
	printf("Producer: %d
", i);
	pthread_mutex_unlock(&mutexB);
	pthread_mutex_unlock(&mutexA);
    }
    pthread_exit(0);
    return NULL;
}

void * consumer(void * ptr) {
    /*
    int i;
    for (i = 1; i <= MAX; i++) {
        pthread_mutex_lock(&the_mutex);
        while (buffer == 0) {
            pthread_cond_wait(&condc, &the_mutex);
        }
    
        printf("%d verbraucht
", buffer);
        buffer = 0;
    
        pthread_cond_signal(&condp);
        pthread_mutex_unlock(&the_mutex);
    }
    pthread_exit(0);
    */
    int i;
    for (i = 1; i <= MAX; i++) {
	pthread_mutex_lock(&mutexB);
	/* printf("Es droht Gefahr..
"); // Forcierung */
	pthread_mutex_lock(&mutexA);
	printf("Consumer: %d
", i);
	pthread_mutex_unlock(&mutexA);
	pthread_mutex_unlock(&mutexB);
    }
    pthread_exit(0);
    return NULL;
}

int main(int argc, char ** argv) {
    pthread_t pro, con;
    
    /* pthread_mutex_init(&the_mutex, NULL); */
    pthread_mutex_init(&mutexA, NULL);
    pthread_mutex_init(&mutexB, NULL);
    pthread_cond_init(&condc, NULL);
    pthread_cond_init(&condp, NULL);
    
    pthread_create(&con, NULL, consumer, NULL);
    pthread_create(&pro, NULL, producer, NULL);
    
    pthread_join(con, NULL);
    pthread_join(pro, NULL);
    
    /* pthread_mutex_destroy(&the_mutex);	 Free up the_mutex */
    pthread_mutex_destroy(&mutexA);
    pthread_mutex_destroy(&mutexB);
    pthread_cond_destroy(&condc);		/* Free up consumer condition variable */
    pthread_cond_destroy(&condp);		/* Free up producer condition variable */
    
    return 0;
}```

Damit wird es forciert. Jetzt verstehe ich nicht, wieso die Kernlast nicht auf 100 % geht?
#3

Der erste Code besitzt den Fehler, dass pthread_join falsch aufgerufen wird. Korrekt wäre es, wenn stehen würde:

pthread_join(pro, NULL);

Ein Deadlock bedeutet noch lange nicht das die Kernlast auf 100% Prozent geht. Dieser verursacht lediglich, dass das Programm unfähig ist weitere Anfragen zu beantworten, die über ein Mutex laufen. Wenn du etwas Effektiveres willst um dein System zu überlasten, würde ich wohl eher zu einer Forkbomb raten.

#4

Dankeschön.

Mit pthread_join ist mir auch aufgefallen, und in Variante 2 hab ich es verbessert.

Umgangssprachlich friert das Programm einfach ein, Aaaber wie kann ich es schaffen, dass aktiv versucht wird, in Mutex A und in Mutex B zu gelangen (CPU-Last 100 %, Livelock ? )?

Und ich bräuchte noch etwas Theoretisches über gemeinsam genutzte Ressourcen, Deadlocks usw. Wir bekommen Aufgaben, die etwas so lauten: Veranschaulichen sie das Dining Philosophers Problem.

Was es genau sein wird: keine Ahnung.

#5
#include <stdbool.h>
#include <pthread.h>

#define MAX 1000000	/* Numbers to produce */
volatile int lockA;
volatile int lockB;
pthread_mutex_t the_mutex;
pthread_cond_t condc, condp;
int buffer = 0;

void spinlock_lock(volatile int *lock) {
    while (__sync_val_compare_and_swap(lock, 0, 1));
}

void spinlock_unlock(volatile int *lock) {
    *lock = 0;
}

void * producer(void * ptr) {
    int i;
    for (i = 1; i <= MAX; i++) {
        spinlock_lock(&lockA);
        spinlock_lock(&lockB);
        printf("Producer: %d
", i);
        spinlock_unlock(&lockB);
        spinlock_unlock(&lockA);
    }
    pthread_exit(0);
    return NULL;
}

void * consumer(void * ptr) {
    /*
    int i;
    for (i = 1; i <= MAX; i++) {
    pthread_mutex_lock(&the_mutex);
    while (buffer == 0) {
    pthread_cond_wait(&condc, &the_mutex);
    }

    printf("%d verbraucht
", buffer);
    buffer = 0;

    pthread_cond_signal(&condp);
    pthread_mutex_unlock(&the_mutex);
    }
    pthread_exit(0);
    */
    int i;
    for (i = 1; i <= MAX; i++) {
        spinlock_lock(&lockB);
        /* printf("Es droht Gefahr..
"); // Forcierung */
        spinlock_lock(&lockA);
        printf("Consumer: %d
", i);
        spinlock_unlock(&lockA);
        spinlock_unlock(&lockB);
    }
    pthread_exit(0);
    return NULL;
}

int main(int argc, char ** argv) {
    pthread_t pro, con;

    lockA = 0;
    lockB = 0;

    pthread_create(&con, NULL, consumer, NULL);
    pthread_create(&pro, NULL, producer, NULL);

    pthread_join(con, NULL);
    pthread_join(pro, NULL);

    pthread_cond_destroy(&condc);	/* Free up consumer condition variable */
    pthread_cond_destroy(&condp);	/* Free up producer condition variable */

    return 0;
}

*** Edit ***

Nochmal ohne selbst implementierten Spinlock:

#include <stdbool.h>
#include <pthread.h>

#define MAX 1000000	/* Numbers to produce */
pthread_spinlock_t spinlockA;
pthread_spinlock_t spinlockB;
pthread_cond_t condc, condp;
int buffer = 0;

void * producer(void * ptr) {
    int i;
    for (i = 1; i <= MAX; i++) {
        pthread_spin_lock(&spinlockA);
        pthread_spin_lock(&spinlockB);
        printf("Producer: %d
", i);
        pthread_spin_unlock(&spinlockB);
        pthread_spin_unlock(&spinlockA);
    }
    pthread_exit(0);
    return NULL;
}

void * consumer(void * ptr) {
    /*
    int i;
    for (i = 1; i <= MAX; i++) {
    pthread_mutex_lock(&the_mutex);
    while (buffer == 0) {
    pthread_cond_wait(&condc, &the_mutex);
    }

    printf("%d verbraucht
", buffer);
    buffer = 0;

    pthread_cond_signal(&condp);
    pthread_mutex_unlock(&the_mutex);
    }
    pthread_exit(0);
    */
    int i;
    for (i = 1; i <= MAX; i++) {
        pthread_spin_lock(&spinlockB);
        pthread_spin_lock(&spinlockA);
        printf("Consumer: %d
", i);
        pthread_spin_lock(&spinlockA);
        pthread_spin_lock(&spinlockB);
    }
    pthread_exit(0);
    return NULL;
}

int main(int argc, char ** argv) {
    pthread_t pro, con;

    pthread_spin_init(&spinlockA, PTHREAD_PROCESS_PRIVATE);
    pthread_spin_init(&spinlockB, PTHREAD_PROCESS_PRIVATE);

    pthread_create(&con, NULL, consumer, NULL);
    pthread_create(&pro, NULL, producer, NULL);

    pthread_join(con, NULL);
    pthread_join(pro, NULL);

    pthread_cond_destroy(&condc);	/* Free up consumer condition variable */
    pthread_cond_destroy(&condp);	/* Free up producer condition variable */

    return 0;
}

*** Edit ***

Zum theoretischen von genutzte Ressourcen, Deadlocks usw. kann ich leider nichts anbieten, aber das Problem der Dining Philophers wird in Rust Book gut veranschaulicht: https://doc.rust-lang.org/1.2.0/book/dining-philosophers.html

#6

Dankeschön, ich hab es jetzt verstanden/kapiert. :slight_smile:

Was gibt es neben P und C Problem denn noch für nebenläufige Aufgabenstellungen? Ich werd im Internet/google einfach mal danach suchen. Es wird wahrscheinlich nicht PCP drankommen, das wäre ein glücksfall. Aber es wäre wahrscheinlich auch 0 Punkte, wenn ich zu Dining Philophers einfach ein Bild malen würde. :frowning:

Ich meld mich, falls ich das nicht mehr verstehen sollte, nochmal.

#7

Ich weiß nicht, ob du Java von Kopf bis Fuß kennst. Da gibt es jedenfalls das Rainer und Monika Beispiel. Das Paar Rainer und Monika hat Geldprobleme und muss schauen, dass sie nicht das gemeinsame Bankkonto überziehen. Du kannst dir den Quellcode anschauen: Index of /german_examples/hfjava2ger in der Zip javavonkopfbisfuss.zip im Ordner kapitel_15 in der Datei RainerUndMonika.java