条件变量从未初始化/陷入死锁

时间:2017-05-06 18:10:37

标签: c multithreading pthreads

不久之前,我测试了信号量使用简单程序的方式,其中3个线程(每个线程由其函数调用)以输出的方式同步:

<ONE><TWO><THREE><ONE><TWO><THREE><ONE><TWO><THREE><ONE><TWO><THREE><ONE><TWO><THREE>...

(感兴趣的每个人的代码here或作为参考点)

我尝试使用条件变量重新创建相同的程序,并且事情变得非常棘手

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


pthread_cond_t condvar1=PTHREAD_COND_INITIALIZER;
pthread_cond_t condvar2=PTHREAD_COND_INITIALIZER;
pthread_cond_t condvar3=PTHREAD_COND_INITIALIZER;

pthread_mutex_t MUT=PTHREAD_MUTEX_INITIALIZER;

int loop=3;

void *one()
{
    int i;

    for(i=0;i<loop;i++)
    {
        pthread_mutex_lock(&MUT);
        if(pthread_cond_signal(&condvar3)==0)
                    pthread_cond_wait(&condvar1, &MUT);    
        pthread_mutex_unlock(&MUT);

                printf("<ONE>");

        pthread_mutex_lock(&MUT);    
            pthread_cond_signal(&condvar2);    
        pthread_mutex_unlock(&MUT);
    }

    return(NULL);
}

void *two()
{
    int i;

    for(i=0;i<loop;i++)
    {
        pthread_mutex_lock(&MUT);
        if(pthread_cond_signal(&condvar1)==0)  
                    pthread_cond_wait(&condvar2, &MUT);    
        pthread_mutex_unlock(&MUT);

                printf("<TWO>");

        pthread_mutex_lock(&MUT);    
            pthread_cond_signal(&condvar3);    
        pthread_mutex_unlock(&MUT);
    }

    return(NULL);
}

void *three()
{
    int i;

    for(i=0;i<loop;i++)
    {
        pthread_mutex_lock(&MUT);
        if(pthread_cond_signal(&condvar2)==0)   
                    pthread_cond_wait(&condvar3, &MUT);    
        pthread_mutex_unlock(&MUT);

                printf("<THREE>");

        pthread_mutex_lock(&MUT);    
            pthread_cond_signal(&condvar1);    
        pthread_mutex_unlock(&MUT);
    }

    return(NULL);
}


int main(int argc, char *argv[])
{
    pthread_t ena, dyo, tria;

    pthread_create(&ena, NULL, one, NULL);
    pthread_create(&dyo, NULL, two, NULL);
    pthread_create(&tria, NULL, three, NULL);

    pthread_join(ena, NULL);
    pthread_join(dyo, NULL);
    pthread_join(tria, NULL);

    return 0;
}

基本上我遇到了初始化第一个变量(condvar1)的问题,以便开始同步,因为非常方便的sem_init()是不可能的。

后来我虽然除此之外,我可以检查之前的条件变量(if(pthread_cond_signal(&previousconditionvariable)==0))是否已经释放其锁定然后我可以锁定 next 条件变量。

但是,对于condvar1没有正确的初始化(因为我搜索了herehereherehere),当然没有输出。

有关初始化的想法和/或检查先前'条件变量信号的提示吗?

更新:删除了之前的ifs并在他们的位置获得了一个标志:

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


pthread_cond_t condvar1=PTHREAD_COND_INITIALIZER;
pthread_cond_t condvar2=PTHREAD_COND_INITIALIZER;
pthread_cond_t condvar3=PTHREAD_COND_INITIALIZER;

pthread_mutex_t MUT=PTHREAD_MUTEX_INITIALIZER;


int flag;
int loop=3;

void *one()
{
    int i;

    for(i=0;i<loop;i++)
    {
        pthread_mutex_lock(&MUT);
        if(flag==1)
        {
                    pthread_cond_wait(&condvar1, &MUT);
        }   
        pthread_mutex_unlock(&MUT);

                printf("<ONE>");
        flag=2;

        pthread_mutex_lock(&MUT);    
            pthread_cond_signal(&condvar2);    
        pthread_mutex_unlock(&MUT);
    }

    return(NULL);
}

void *two()
{
    int i;

    for(i=0;i<loop;i++)
    {
        pthread_mutex_lock(&MUT);
        if(flag==2)  
        {
                    pthread_cond_wait(&condvar2, &MUT);

        }   
        pthread_mutex_unlock(&MUT);

                printf("<TWO>");
        flag=3;

        pthread_mutex_lock(&MUT);    
            pthread_cond_signal(&condvar3);    
        pthread_mutex_unlock(&MUT);
    }

    return(NULL);
}

void *three()
{
    int i;

    for(i=0;i<loop;i++)
    {
        pthread_mutex_lock(&MUT);
        if(flag==3)  
        {
                    pthread_cond_wait(&condvar3, &MUT);

        }    
        pthread_mutex_unlock(&MUT);

                printf("<THREE>");
        flag=1;

        pthread_mutex_lock(&MUT);    
            pthread_cond_signal(&condvar1);    
        pthread_mutex_unlock(&MUT);
    }

    return(NULL);
}


int main(int argc, char *argv[])
{
    pthread_t ena, dyo, tria;

    flag=1;

    pthread_create(&ena, NULL, one, NULL);
    pthread_create(&dyo, NULL, two, NULL);
    pthread_create(&tria, NULL, three, NULL);

    pthread_join(ena, NULL);
    pthread_join(dyo, NULL);
    pthread_join(tria, NULL);

    return 0;
}

现在输出为<THREE><THREE><THREE><TWO><TWO><TWO><ONE><ONE><ONE>

1 个答案:

答案 0 :(得分:1)

此代码是对代码的简化,我相信它可以按预期工作。有一个单一的线程函数,由其参数控制。也只有一个条件变量。 turn变量指示它转向哪个线程。 turn的操作是在线程锁定互斥锁时完成的,因此没有其他线程同时使用它。同样在互斥锁被锁定时完成打印,以确保它在正确的时间发生。

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

static pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t MUT = PTHREAD_MUTEX_INITIALIZER;

static int turn;
static int loop = 3;

struct Control
{
    int thread_num;
    const char *msg;
};

static void *thread_func(void *arg)
{
    struct Control *ctl = arg;
    printf("Thread %d (%s)\n", ctl->thread_num, ctl->msg);

    for (int i = 0; i < loop; i++)
    {
        pthread_mutex_lock(&MUT);
        while (turn != ctl->thread_num)
            pthread_cond_wait(&condvar, &MUT);

        printf("<%s>", ctl->msg);
        turn++;
        if (turn > 3)
            turn = 1;
        pthread_cond_broadcast(&condvar);

        pthread_mutex_unlock(&MUT);
    }

    return(NULL);
}

int main(void)
{
    pthread_t ena, dyo, tri;
    struct Control ctl[] =
    {
        { 1, "ONE"   },
        { 2, "TWO"   },
        { 3, "THREE" },
    };

    srand(time(0));
    turn = rand() % 3 + 1;
    printf("%d goes first\n", turn);

    pthread_create(&ena, NULL, thread_func, &ctl[0]);
    pthread_create(&dyo, NULL, thread_func, &ctl[1]);
    pthread_create(&tri, NULL, thread_func, &ctl[2]);

    pthread_join(ena, NULL);
    pthread_join(dyo, NULL);
    pthread_join(tri, NULL);

    putchar('\n');

    return 0;
}

在我的MacBook Pro上运行macOS Sierra 10.12.4(使用GCC 7.1.0,而不是编译器在这里非常重要),我从连续运行中获得了这样的结果(但是在两者之间存在不确定的多秒差距运行):

2 goes first
Thread 1 (ONE)
Thread 2 (TWO)
Thread 3 (THREE)
<TWO><THREE><ONE><TWO><THREE><ONE><TWO><THREE><ONE>

3 goes first
Thread 1 (ONE)
Thread 2 (TWO)
Thread 3 (THREE)
<THREE><ONE><TWO><THREE><ONE><TWO><THREE><ONE><TWO>

3 goes first
Thread 1 (ONE)
Thread 2 (TWO)
Thread 3 (THREE)
<THREE><ONE><TWO><THREE><ONE><TWO><THREE><ONE><TWO>

1 goes first
Thread 1 (ONE)
Thread 2 (TWO)
Thread 3 (THREE)
<ONE><TWO><THREE><ONE><TWO><THREE><ONE><TWO><THREE>

3 goes first
Thread 2 (TWO)
Thread 1 (ONE)
Thread 3 (THREE)
<THREE><ONE><TWO><THREE><ONE><TWO><THREE><ONE><TWO>

请注意,有一次,线程2在线程1之前报告。

我希望可以像原始代码一样使用三个条件变量,但这似乎有些过分。

pthread调用的错误检查是......不存在。这在示例代码中是好的,至少在它工作时,但通常不是一个好主意。