简体   繁体   中英

C Semaphore deadlock

I wrote the following code for leetcode question 1116. It's supposed to print out something like 01020304.... However my program is not printing anything now. I used the semaphore signals to post and wait signals and a variable to record current printing value.

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

typedef struct {
    int n;
    volatile int cur;
    sem_t szero;
    sem_t sodd;
    sem_t sevn;
} ZeroEvenOdd;

ZeroEvenOdd* zeroEvenOddCreate(int n) {
    ZeroEvenOdd* obj = (ZeroEvenOdd*) malloc(sizeof(ZeroEvenOdd));
    obj->n = n;

    obj->cur = 1;
    sem_init(&obj->szero, 0, 1);
    sem_init(&obj->sodd, 0, 0);
    sem_init(&obj->sevn, 0, 0);
    return obj;
}

// You may call global function `void printNumber(int x)`
// to output "x", where x is an integer.

void printNumber(int x) {
    printf("%d", x);
}

void zero(ZeroEvenOdd* obj) {
    while(obj->cur<obj->n) {
        sem_wait(&obj->szero);
        printNumber(0);
        printNumber(obj->cur);
        if (obj->cur%2==1) {
            sem_post(&obj->sodd);
        } else {
            sem_post(&obj->sevn);
        }
    }
    
}

void even(ZeroEvenOdd* obj) {
    while(obj->cur<=obj->n){
        sem_wait(&obj->sevn);
        printNumber(obj->cur);
        obj->cur++;
        sem_post(&obj->szero);
    }
}

void odd(ZeroEvenOdd* obj) {
    while(obj->cur<=obj->n) {
        sem_wait(&obj->sodd);
        printNumber(obj->cur);
        obj->cur++;
        sem_post(&obj->szero);
    }
}

void zeroEvenOddFree(ZeroEvenOdd* obj) {
    if(NULL!=obj) {
        sem_destroy(&obj->szero);
        sem_destroy(&obj->sodd);
        sem_destroy(&obj->sevn);
        free(obj);
    }
}

int main() {
    ZeroEvenOdd *f=zeroEvenOddCreate(5);
    pthread_t tidz, tido, tide;

    pthread_create(&tidz, NULL, (void *)&zero, (void *)f);
    pthread_create(&tido, NULL, (void *)&odd, (void *)f);
    pthread_create(&tide, NULL, (void *)&even, (void *)f);

    

    pthread_join(tidz, NULL);
    pthread_join(tido, NULL);
    pthread_join(tide, NULL);
}

What is the problem? Also is there a good way to debug this kind of problem using GDB?

The logic:

cur    n     zero           even           odd
 0     5     post(even)     print,sleep     -
 1     5     post(odd)      -              print,sleep
 2     5     post(even)     print,sleep     -
 3     5     post(odd)      -              print,sleep
 4     5     post(even)     print,exit      -
 5     5     exit.

so odd still thinks that obj->cur == 4 [ which is what it was when it went to sleep ]. There are two logical problems; even & odd terminate on <=, while zero terminates on <. Zero needs to 'unstick' the other one before exiting; so if last posted even, it needs to post odd.

Lastly, you are on thin ice by checking the condition without some form of synchronization; that means your check could be stale on the first iteration. In a simple counter case it doesn't matter, but a more complicated condition could be a problem, and your program contains no indication that it needs protection.

Try the following patch:

--- s.c 2020-12-10 14:27:25.518641527 +0000
+++ s2.c    2020-12-10 14:28:15.358562490 +0000
@@ -29,23 +29,27 @@
     printf("%d", x);
 }
 
+void sig(ZeroEvenOdd* obj) {
+    if (obj->cur%2==1) {
+        sem_post(&obj->sodd);
+    } else {
+        sem_post(&obj->sevn);
+    }
+}
+
 void *zero(ZeroEvenOdd* obj) {
+    sem_wait(&obj->szero);
     while(obj->cur<obj->n) {
-        sem_wait(&obj->szero);
         printNumber(0);
         printNumber(obj->cur);
-        if (obj->cur%2==1) {
-            sem_post(&obj->sodd);
-        } else {
-            sem_post(&obj->sevn);
-        }
+   sig(obj);
+        sem_wait(&obj->szero);
     }
-   
-    
+    sig(obj);
 }
 
 void *even(ZeroEvenOdd* obj) {
-    while(obj->cur<=obj->n){
+    while(obj->cur<obj->n){
         sem_wait(&obj->sevn);
         printNumber(obj->cur);
         obj->cur++;
@@ -55,13 +59,13 @@
 }
 
 void *odd(ZeroEvenOdd* obj) {
-    while(obj->cur<=obj->n) {
+    while(obj->cur<obj->n) {
         sem_wait(&obj->sodd);
         printNumber(obj->cur);
         obj->cur++;
         sem_post(&obj->szero);
     }
-   
+
 }
 
 void zeroEvenOddFree(ZeroEvenOdd* obj) {
@@ -84,5 +88,5 @@
     pthread_join(tidz, NULL);
     pthread_join(tido, NULL);
     pthread_join(tide, NULL);
-
+    putchar('\n');
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM