pthread中的互斥锁、条件变量与读写锁

  上过操作系统课程的同学应该都知道在线程,管理各个线程运行,实现对资源的互斥访问的时候有一个互斥锁mutex,主要防止在OS对线程进行切换的时候,由于每个线程执行的语句的深度不一样,结果导致对某些共享量的操作乱套了,实际上是因为在重新载入线程时,恢复上下文导致了原本已经修改过的变量又一次被原来的值所替代(相当于这个变量对于当前线程而言没有发生变化,实际上已经变了,解释可能不准确,不单单只有这种情况)。所以就有了互斥锁,在对一个共享资源访问的时候,严格保证一次只有一个线程能够获得这个资源的使用权,因此就不会出现多个线程同时操作同一资源的情况,也就搞定了共享的问题。但是与此同时,又涉及到一个效率的问题,如果一个线程拿着锁,另一个线程一直在那儿等着也不是回事,不停地询问这把锁的使用权(是不是到我啦),所以出现了条件变量来提高效率。

       先上代码,场景是出租车疑问,一位乘客等待出租车,出租车先后到来的顺序以及乘客的顺序

    

#include <iostream>
#include<pthread.h>
#include<unistd.h>

using namespace std;
pthread_mutex_t mutex_t = PTHREAD_MUTEX_INITIALIZER;    //静态初始化互斥锁
pthread_cond_t cont_t = PTHREAD_COND_INITIALIZER;    //静态初始化条件变量
int count = 0;

void *passenger(void *arg)
{
    cout<<"Passenger " << (char *)arg << " arrived"<<endl;
    pthread_mutex_lock(&mutex_t);
    ++count;
    pthread_cond_wait(&cont_t, &mutex_t);
    cout<<"Passenger " << (char *)arg << " get on."<<endl;
    pthread_mutex_unlock(&mutex_t);
    pthread_exit((void *)1);
}

void *taxi(void *arg)
{
    cout<<"Taxi " << (char *)arg << " arrived."<<endl;
    int tmp = 0;
    while(1){    //循环保证先来的出租车也可以检测到后来的乘客,虽说是一个等待的队列
        pthread_mutex_lock(&mutex_t);
        if(count > 0){
            pthread_cond_signal(&cont_t);
            pthread_mutex_unlock(&mutex_t);
            --count;
            break;
        }
        pthread_mutex_unlock(&mutex_t);
        ++tmp;
        sleep(1);
        if(tmp > 10) break;        //等待时间总有些上限的吧
    }
    cout<<"Taxi "<<(char *)arg <<" get the passenger."<<endl;
    pthread_exit((void *)0);
}


bool isCreateThreadFailed(int flag)
{
    if(flag){
        cout<<"Create thread failed"<<endl;
        return false;
    }

    return true;
}

int main()
{
    pthread_t tid[3];
    int ret;

    ret = pthread_create(&tid[0], NULL, taxi, (void *)("Sakura"));
    if(!isCreateThreadFailed(ret)) return ret;
    sleep(1);
    cout<<"Time passing"<<endl;

    ret = pthread_create(&tid[1], NULL, passenger, (void *)("Luncy"));
    if(!isCreateThreadFailed(ret)) return ret;
    sleep(1);
    cout<<"Time passing"<<endl;

    ret = pthread_create(&tid[2], NULL, taxi, (void *)("BlueLigh"));
    if(!isCreateThreadFailed(ret)) return ret;
    sleep(1);
    cout<<"Time passing"<<endl;

    void *tmp;
    for(int i = 0; i < 3; ++i){
        ret = pthread_join(tid[i], &tmp);
        if(ret){
            cout<<"Join thread failed. tid = " << tid[i] << ", retval = " << ret <<endl;
            return ret;
        }
        cout<<"retval = "<< tmp << endl;
    }

    cout<<"Over!"<<endl;

    return 0;

}








  1、互斥锁:一个特殊的变量,有锁上和打开两个状态。一般被设置为全局变量,如果线程持有某把锁,即是给该锁上锁,那么只有该线程可以打开这把锁,在lock和unlock之间,放置共享资源可以保证一次只有一个线程在访问,所以不会发生冲突这一问题。

 
 
pthread_mutex_t //声明锁
int pthread_mutex_lock ( pthread_mutex_t * ) //上锁,阻塞函数
int pthread_mutex_trylock ( pthread_mutex_t * );    //尝试上锁,当锁被上时返回 EBUSY
int pthread_mutex_unlock ( pthread_mutex_t * ) //解锁

2、共享变量:由于互斥锁的不足,上文已经介绍过了。通过设置条件变量,可以让一些条件为满足的线程暂时进入睡眠状态,等到条件满足时由其它线程发送信号唤醒。条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其他的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程,这些线程将重新锁定互斥锁并重新测试条件是否满足。

pthread_cont_t  //声明条件变量
int pthread_cont_wait(pthread_cont_t *, pthread_mutex_t *)  //条件等待
int pthread_cont_timedwait(pthread_cont_t *, pthread_mutex_t *, const struct timespec *)    //计时等待
int pthread_cond_signal(pthread_cont_t *)   //发送信号
int pthread_cond_destroy(pthread_cont_t *)  //注销

3、读写锁:对某些资源的访问会出现两种情况,一种是访问必须是排他性的,就是独占的意思,称作写操作;另一种情况就是访问方式可以是共享的,就是说可以有多个线程同时去访问某个资源,称为读操作。读写锁比起互斥锁具有更高的适用性与并行性,可以有多个线程同时占用读模式的读写锁,但是只能有一个线程占用写模式的读写锁

①当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞

②当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行加锁的线程将会被阻塞

③当读写锁在读模式的锁状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁的请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求长期阻塞。 

pthread_rwlock_t    //声明读写锁
int pthread_rwlock_init(pthread_rwlock_t *, const pthread_rwlock_attr_t *) //动态初始化
int pthread_rwlock_destory(pthread_rwlock_t *)  //注销锁
int pthread_rwlock_rdlock(pthread_rwlock_t *)   //获取读出锁
int pthread_rwlock_wrlock(pthread_rwlock_t *)   //获取写入锁
int pthread_rwlock_unlock(pthread_rwlock_t *)   //解锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *)   //尝试获取读出锁,非阻塞,失败返回 EBUSY
int pthread_rwlock_trywrlock(pthread_rwlock_t *)   //尝试获取写入锁,非阻塞,失败返回 EBUSY


    记这些基础的其实简单的提一下就可以,关键是程序设计的思维,比如代码中的while(1)那个tick。这个更重要,在琐与信号的使用中,个人觉得如何设计一个良好的结构,使各个线程之间能够对共享资源进行互斥访问,同时又不会出现死锁才是一个重点。应该有一些经典的设计方案,目前还没有达到那个功力,以后看到了再整理一下。


猜你喜欢

转载自blog.csdn.net/qq_37925512/article/details/80538135
今日推荐