Linux中的线程锁与条件变量

线程锁

初始化

Linux中使用数据类型pthread_mutex_t,线程锁的初始化有两种方式:

  • 静态初始化:在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
  • 动态初始化:动态初始化使用pthread_mutex_init(),该函数的声明如下:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。

加锁

使用如下函数加锁:

 int pthread_mutex_lock(pthread_mutex *mutex);
 int pthread_mutex_trylock(pthread_mutex_t *mutex);

解锁

在完成对共享资源的访问,要解锁:

int pthread_mutex_unlock(pthread_mutex_t *mutex);

销毁锁

在锁使用完后,要进行销毁:

 int pthread_mutex_destroy(pthread_mutex *mutex);

需要说明的是:在pthread_mutex_lockpthread_mutex_unlock之间的代码属于临界区,只能被当前线程访问和操作。

条件变量

等待条件

有两种等待条件方式,无论哪种等待方式都必须结合一个线程锁:

  • 无条件等待
int pthread_cond_wait(pthread_cond_t *cond, pthrad_mutex_t *mutex);
  • 计时等待
int pthread_cond_timedwait(pthread_cond_t *cond, pthrad_mutex_t *mutex,const struct timespec *abstime);

当等待时间超过给定时刻条件没有成立,则返回ETIMEOUT,结束等待

在调用pthread_cond_wait前,必须由本线程加锁,
在调用pthread_cond_wait时,如果等待的条件不成立,则线程自动阻塞,并释放等待状态改变的互斥锁,此时另一个线程便可以修改该条件,如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。 而当此线程被唤醒时, 将自动将这个mutex加锁。

激发条件有两种形式:
1. pthread_cond_signal()按照进入等待队列的顺序激活一个等待线程
2. pthread_cond_broadcast()激活所有等待线程

下面的程序是一个演示:

#include <stdio.h>
#include <pthread.h>
#include "stdlib.h"
#include "unistd.h"
pthread_mutex_t mutex;
pthread_cond_t cond;
void hander(void *arg)
{
    //free(arg);
    printf("Cleanup handler of second thread.\n");
    (void)pthread_mutex_unlock(&mutex);
}
void *thread1(void *arg)
{
    //当pthread_cleanup_push与pthread_clean_pop之间的代码正在执行时线程被中止,自动执行hander函数,两者必须成对出现
    pthread_cleanup_push(hander, &mutex);
    while(1)
    {
        printf("thread1 is running\n");
        pthread_mutex_lock(&mutex);
        printf("thread1 got mutex!\n");
        pthread_cond_wait(&cond, &mutex);
        printf("thread1 applied the condition\n");
        pthread_mutex_unlock(&mutex);
        sleep(4);
    }
    pthread_cleanup_pop(0);//当参数为0表示执行从函数栈中弹出的函数,不为0表示不执行
}
void *thread2(void *arg)
{
    while(1)
    {
        //sleep(10);
        printf("thread2 is running\n");
        pthread_mutex_lock(&mutex);
        printf("thread2 got mutex!\n");
        pthread_cond_wait(&cond, &mutex);
        printf("thread2 applied the condition\n");
        pthread_mutex_unlock(&mutex);
        sleep(2);
    }
}
int main()
{
    pthread_t thid1,thid2;
    printf("condition variable study!\n");
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    pthread_create(&thid1, NULL, thread1, NULL);
    pthread_create(&thid2, NULL, thread2, NULL);
    //sleep(1);
    do
    {
        sleep(2);
        pthread_cancel(thid1);
        sleep(2);
        pthread_cond_signal(&cond);
    }while(1);
    sleep(20);
    pthread_exit(0);
    return 0;
}

上面的程序输出如下,2个线程thread1,thread2都不会被阻塞,输出如下:

condition variable study!
thread1 is running
thread1 got mutex!
thread1 applied the condition
thread2 is running
Cleanup handler of second thread.
thread2 got mutex!
thread2 applied the condition
thread2 is running
thread2 got mutex!
thread2 applied the condition
thread2 is running
thread2 got mutex!
.
.
.

因为在取消线程一时,会自动调用handler释放锁;而如果将上面的程序中`void *thread1(void *arg)中的pthread_cleanup_push(hander, &mutex)pthread_cleanup_pop()去掉:

#include <stdio.h>
#include <pthread.h>
#include "stdlib.h"
#include "unistd.h"
pthread_mutex_t mutex;
pthread_cond_t cond;
void hander(void *arg)
{
    //free(arg);
    printf("Cleanup handler of second thread.\n");
    (void)pthread_mutex_unlock(&mutex);
}
void *thread1(void *arg)
{
    //当pthread_cleanup_push与pthread_clean_pop之间的代码正在执行时线程被中止,自动执行hander函数
    pthread_cleanup_push(hander, &mutex);
    while(1)
    {
        printf("thread1 is running\n");
        pthread_mutex_lock(&mutex);
        printf("thread1 got mutex!\n");
        pthread_cond_wait(&cond, &mutex);
        printf("thread1 applied the condition\n");
        sleep(4);//使得线程1在此处被取消,此时mutex还未被释放
        pthread_mutex_unlock(&mutex);
        sleep(4);
    }
    pthread_cleanup_pop(0);
}
void *thread2(void *arg)
{
    while(1)
    {
        sleep(4);
        printf("thread2 is running\n");
        pthread_mutex_lock(&mutex);//线程2此时无法获得mutex
        printf("thread2 got mutex!\n");
        pthread_cond_wait(&cond, &mutex);
        printf("thread2 applied the condition\n");
        pthread_mutex_unlock(&mutex);
        sleep(2);
    }
}
int main()
{
    pthread_t thid1,thid2;
    printf("condition variable study!\n");
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    pthread_create(&thid1, NULL, thread1, NULL);
    pthread_create(&thid2, NULL, thread2, NULL);
    //sleep(1);
    do
    {
        sleep(2);//使得线程1有足够时间进入pthread_cond_wait
        pthread_cond_signal(&cond);//cancel thread1
        sleep(2);
        pthread_cancel(thid1);//


    }while(1);
    sleep(20);
    pthread_exit(0);
    return 0;
}

线程2被阻塞,输出为:

condition variable study!
thread1 is running
thread1 got mutex!
thread1 applied the condition
thread2 is running

为什么呢?我们来分析一下:
当线程1的pthread_cond_wait(&cond, &mutex);等待条件变量成立后,又自动为条件变量加锁,进入sleep(4),而此时主线程运行到pthread_cancel(thid1);,将线程1取消,此时mutex没有被释放,因此thread2中无法获取这个条件变量的锁,如果mutex已经被锁住,调用pthread_mutex_lock(&mutex)函数的线程阻塞在此处直到mutex被释放为止。如果增加pthread_cleanup_push,pthread_cleanup_pop,则在主线程pthread_cancel线程1时,会调用handler来释放mutex,这样线程2就可以获得条件变量的锁,不会被阻塞了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值