关于互斥量和条件变量

条件变量一般用于阻塞线程以及给线程发信号,解除阻塞。而条件变量往往和互斥量一起使用,因为条件变量在阻塞的时候会有一个加锁解锁的过程。还记得以前写过一个生产者——消费者的实例,那时候对一些问题还不是很理解。下面是一个简单的练习实例,以及最后总结遇到的一些问题。

//wait后面要执行的语句必须在while循环之内
//wait后面必须要解锁
//最后一个函数执行的时候不要解锁
//查看pthread_mutex_destroy返回值
//关于内存泄露


#include  "../common.h"

pthread_t   pid1, pid2, pid3, pid4;
pthread_cond_t    num;
pthread_mutex_t   mutex1; 
int count;
void **state;

void *func1(void *argc)
{   
    //pthread_detach(pthread_self());
    printf("thread1 %lu create success!\n", pthread_self());
    printf("1 wait for signal cmd!\n");
    pthread_mutex_lock(&mutex1);
    while(1)
    {
        while(count == 0)
        {
            printf("1count = %d\n", count);
            printf("=============================\n");
            pthread_cond_wait(&num, &mutex1);
            printf("1 reserve signal, i will exit!\n");
            printf("%lu exit!\n", pthread_self());
            pthread_mutex_unlock(&mutex1);
            pthread_exit((void*)&state);

        }
    }

}

void *func2(void *argc)
{   
    //pthread_detach(pthread_self());
    printf("thread2 %lu create success!\n", pthread_self());
    printf("2 wait for signal cmd!\n");
    pthread_mutex_lock(&mutex1);
    while(1)
    {
        while(count == 0)
        {
            printf("2count = %d\n", count);
            printf("=============================\n");
            pthread_cond_wait(&num, &mutex1);
            printf("2 reserve signal, i will exit!\n");
            printf("%lu exit!\n", pthread_self());
            pthread_mutex_unlock(&mutex1);
            pthread_exit((void*)&state);
        }
    }       


}

void *func3(void *argc)
{   
    //pthread_detach(pthread_self());
    printf("thread3 %lu create success!\n", pthread_self());
    printf("3 wait for signal cmd!\n");
    pthread_mutex_lock(&mutex1);
    while(1)
    {
        while(count == 0)
        {
            printf("3count = %d\n", count);
            printf("=============================\n");
            pthread_cond_wait(&num, &mutex1);
            printf("3 reserve signal, i will exit!\n");
            printf("%lu exit!\n", pthread_self());
            pthread_mutex_unlock(&mutex1);
            pthread_exit((void*)&state);
        }
    }
}

void *func4(void *argc)
{   
    //pthread_detach(pthread_self());
    printf("thread4 %lu create success!\n", pthread_self());
    printf("4 wait for signal cmd!\n");
    pthread_mutex_lock(&mutex1);
    while(1)
    {
        while(count == 0)
        {
            printf("4count = %d\n", count);
            printf("=============================\n");
            pthread_cond_wait(&num, &mutex1);
            printf("4 reserve signal, i will exit!\n");
            printf("%lu exit!\n", pthread_self());
            //pthread_mutex_unlock(&mutex1);
            pthread_exit((void*)&state);
        }
    }
}

void add()
{
    printf("alls go go go!\n");
    if (count == 0)
    {
        count++;
        pthread_cond_broadcast(&num);
        pthread_mutex_unlock(&mutex1);
        printf("send signal success!\n");
    }
}

int main(int argc, char **argv)
{
    void **state;
    pthread_mutex_init(&mutex1, NULL);
    pthread_cond_init(&num, NULL);
    pthread_create(&pid1, NULL, func1, NULL);
    sleep(1);
    pthread_create(&pid2, NULL, func2, NULL);
    sleep(1);
    pthread_create(&pid3, NULL, func3, NULL);
    sleep(1);
    pthread_create(&pid4, NULL, func4, NULL);
    sleep(1);
    add();
    #if 1
    if (pthread_join(pid1, (void*)&state) != 0)
    {
        error("pthread_join1");
    }
    else
    {
        printf("pthread join 1 success!\n");
        printf("1 = %d\n", state);
    }
    if (pthread_join(pid2, (void*)&state) != 0)
    {
        error("pthread_join2");
    }
    else
    {
        printf("pthread join 2 success!\n");
        printf("2 = %d\n", state);
    }
    if (pthread_join(pid3, (void*)&state) != 0)
    {
        error("pthread_join3"); 
    }
    else
    {
        printf("pthread join 3 success!\n");
        printf("3 = %d\n", state);
    }
    if (pthread_join(pid4, (void*)&state) != 0)
    {
        error("pthread_join4"); 
    }
    else
    {
        printf("pthread join 4 success!\n");
        printf("4 = %d\n", state);
    }
    #endif
    sleep(3);
    if (pthread_mutex_destroy(&mutex1) != 0)
    {
        printf("%d\n", pthread_mutex_destroy(&mutex1));   //16
        error("pthread_mutex_destroy");
    }
    if (pthread_cond_destroy(&num) != 0)
    {
        error("pthread_cond_destroy");
    }
    printf("main thread exit!\n");
    printf("%d\n", EBUSY);    //16
    return 0;
}

程序很好理解,就是阻塞四个线程,然后通过广播信号对他们解除阻塞,继续执行完程序。当然,也可以使用pthread_cond_signal单个通知线程。

下面是一个简单的输出:

thread1 77757296 create success!
1 wait for signal cmd!
1count = 0
=============================
thread2 92441456 create success!
2 wait for signal cmd!
2count = 0
=============================
thread3 102931312 create success!
3 wait for signal cmd!
3count = 0
=============================
thread4 113421168 create success!
4 wait for signal cmd!
4count = 0
=============================
alls go go go!
1 reserve signal, i will exit!
2 reserve signal, i will exit!
92441456 exit!
3 reserve signal, i will exit!
102931312 exit!
77757296 exit!
send signal success!
4 reserve signal, i will exit!
113421168 exit!
pthread join 1 success!
1 = 134521716
pthread join 2 success!
2 = 134521716
pthread join 3 success!
3 = 134521716
pthread join 4 success!
4 = 134521716
main thread exit!
16

输出有点乱,因为广播解锁以后,所有阻塞的线程都会竞争锁资源,我们可以适当加延时以看到比较有秩序的输出。

最关键的问题在于wait之前的加锁,广播之后的解锁,以及执行完线程之后的解锁。
pthread_cond_wait之前要加锁,因为此函数会自动解锁资源,将其放于死循环里面的原因是有多个线程等待继续执行,因此我们要不断的判断条件,且在broadcast之后,要改变条件,不然wait会一直阻塞。

关于遇到的问题:

  1. wait解除阻塞后会自动再加锁,因此我们在后面还要对其解锁,不然程序永远只会执行一个线程,因为没有解锁我们的临界资源,其他的wait获取不到。
  2. wait之后要执行的语句需要放在while循环体里面,这样才会执行到
  3. 关于pthread_mutex_destroy的返回值,如果我们最后一个线程执行函数还在占用互斥量,则此函数的返回值为16,并且出错显示success,man手册查看了此函数的出错如下:

    ERRORS
       The pthread_mutex_destroy() function may fail if:
    
       EBUSY  The implementation has detected an attempt to destroy the object referenced by mutex while it is  locked
              or  referenced  (for  example, while being used in a pthread_cond_timedwait() or pthread_cond_wait()) by
              another thread.
    
       EINVAL The value specified by mutex is invalid.
    
       The pthread_mutex_init() function shall fail if:
    
       EAGAIN The system lacked the necessary resources (other than memory) to initialize another mutex.
    
       ENOMEM Insufficient memory exists to initialize the mutex.
      .................................

    可以看见当errno为EBUSY的时候,对应的说明是线程还在占用互斥量资源则会产生此错误,而打印EBUSY的值正好是16,所以按理说每一个线程wait以后的内容应该要给互斥量解锁,但是如果这里给最后一个线程,也就是func4解锁,那么最后的结果就是EBUSY,这个问题很奇怪,目前还未解决。

  4. 对于线程的内存泄漏,这个程序的问题很大, 有很多释放了但是还可以使用的资源,相当与free了指针,但是没有给此指针赋空值,这个问题目前也尚未解决,等以后查找到问题所在,再来看看。希望有看了文章的人,能给出意见。

你可能感兴趣的:(APUE)