linux环境编程之多线程同步

来源:互联网 发布:ubuntu进不去系统 编辑:程序博客网 时间:2024/05/24 03:32

        多线程同步,当有多个线程同时访问共享内存时就会产生数据不一致性。所以为了保证数据的一致性必须让线程同步,同步方式有下面几种:

互斥量

        互斥量从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,访问完后释放互斥量上的锁。对互斥量加锁后,任何其他的线程对互斥量加锁都会被阻塞,直到互斥量上的锁释放。当锁释放后,阻塞的所有线程都会变成可运行的,然后进行抢占。

        互斥量用pthread_mutex_t数据类型来表示,在使用互斥量之前要先对其进行初始化。

        静态初始化: pthread_mutex_t   mutex  =  PTHREAD_MUTEX_INITIALIZER

        动态初始化:int pthread_mutex_init(pthread_mutex_t  *mutex,  const pthread_mutexattr_t *attr);

        注销互斥量:int pthread_mutex_destroy(pthread_mutex_t  *mutex);

        返回值:若成功则返回0,失败则返回错误码;

        第一个参数:是互斥量的锁;注销互斥量函数的第二个参数为锁的属性,一般为NULL;


        初始化后就准备对互斥量上锁了,如果互斥量已经上锁了,那试图上锁的线程将会阻塞直到锁释放。上锁函数如下:

        int pthread_mutex_lock(pthread_mutex_t  *mutex);

        int pthread_mutex_trylock(pthread_mutex_t  *mutex);

        int pthread_mutex_unlock(pthead_mutex_t   *mutex);

        返回值:成功返回0,失败返回错误码;

        pthread_mutex_lock()函数对已经上锁的互斥量上锁会阻塞  直到锁释放。

        pthread_mutex_trylock()函数则不阻塞等待,如果互斥量没有上锁,则上锁返回0;否则,失败返回错误码。


读写锁

        互斥量虽然能有效的保证数据的一致性,但是这降低了数据的并行访问效率。因为互斥量只有两个状态,一个是上锁状态,一个是不上锁状态。如果多个线程都是只读不写,那么如果用互斥量就有点损失性能了。所以也就出现了读写锁,也就是共享-独占锁,当读写锁以读模式锁住,他则是共享的,其他读线程都可以再次上锁访问,这是共享锁。如果以写模式锁住,则是独占锁,这时和互斥量一样了。

        下面是读写锁初始化函数:

        int  pthread_rwlock_init(pthread_rwlock_t  *rwlock,  const  pthread_rwlockattr_t  *attr);

        int  pthread_rwlock_destroy(pthread_rwlock_t  *rwlock);

        返回为0, 失败返回错误码;

 

        在读模式下上读写锁函数:int pthread_rwlock_rdlock(pthread_rwlock_t  *rwlock);

        在写模式下上读写锁函数:int pthread_rwlock_wrlock(pthread_rwlock_t  *rwlock);

        不管在什么模式下,都使用同一个函数释放锁:int pthread_rwlock_unlock(pthread_rwlock_t  *rwlock);

        返回值:成功返回0,失败返回错误码;

        这里读模式就是上读锁的意思,表示该线程只读临界区的数据,而不修改。写模式则是独占锁,可能会修改数据;

        注意:读写锁不像互斥量,读写锁是不会阻塞的。


         和互斥量一样,读写锁也有尝试加锁函数:

         int pthread_rwlock_tryrdlock(pthread_rwlock_t  *rwlock); //读

         int pthread_rwlock_trywrlock(pthread_rwlock_t  *rwlock); //写

         返回值:成功返回0,失败返回错误码;

         其实在读、写模式下的函数已经不再阻塞了,本来可以不用尝试加锁函数的,但是共享锁可能有线程数目的限制,所以可以用尝试加锁来加锁;


条件变量

        条件变量是多线程下用来同步的另外一种机制。条件变量给多线程提供一个会合的场所,可以和互斥量一起使用,允许线程以无竞争的方式等待特定的条件发生。

        条件变量本身是由互斥量保护的,线程在改变条件前必须要锁住互斥量,其他线程因为没有得到锁,所以不知道条件的改变。

       

        条件变量初始化:

        静态初始化:pthread_cond_t  cond = PTHREAD_COND_INITIALIZER;

        动态初始化:pthread_cond_t  cond;

                                int pthread_cond_init(pthread_cond_t  *cond,  pthread_condattr_t  *attr);//初始化函数

                                int pthread_cond_destroy(pthread_cond_t  *cond);//注销条件函数

        返回值:成功返回0,失败返回错误码;

        初始化函数: pthread_cond_init()中的第二个参数为条件变量属性,一般设置NULL;


        条件变量等待函数:

        int  pthread_cond_wait(pthread_cond_t  *cond,  pthread_mutex_t  *mutex);

        int  pthread_cond_timedwait(pthread_cond_t  *cond,  pthread_mutex_t  *mutex, const  struct  timespec  *timeout);

        返回值:成功返回0,失败返回错误码;

        pthread_cond_wait()和pthread_cond_timedwait()函数类似,只是pthread_cond_timedwait()函数有个定时功能,如果在规定时间内条件没有变真,pthread_cond_wait()函数会一直睡眠(他没有时间概念);而pthread_cond_timedwait()则会醒来直接返回错误码。所以这里只分析pthread_cond_wait()函数;

        当线程调用了pthread_cond_wait()函数,则线程会在等待条件的线程列表中休眠,然后解互斥量的锁(等待条件有个隐藏的互斥量的锁),这两个操作都是原子操作。

        为什么要解开互斥量的锁呢?因为调用线程在等待条件变量的改变(要不然他一直睡眠着),如果他又一直锁着,其他线程则无法获取到锁(这是互斥量锁,独占的)也就无法修改条件变量,那么等待线程(也就是他自己)就会死锁了。        

        为什么要原子操作呢?其实pthread_cond_wait()函数应该是这样的,先进入条件变量的等待线程列表中,然后检查条件,不符合,则解开互斥量的锁,睡眠(不知道是先解锁还是先睡眠,但是先检查条件是否符合是肯定的)。所以这一系列操作要用原子操作,因为如果不是原子操作当检查完条件时,其他线程又更改了条件,使得条件满足了。但该线程刚检查完不符合,那么就会解锁睡眠等待了,可其他线程已经更改了条件。这就会造成死锁了。

        当条件符合后,pthread_cond_wait()就会醒来,在该函数返回时,互斥量会被再次被锁住。


        条件变量唤醒函数:

        int  pthread_cond_signal(pthread_cond_t  *cond);// 唤醒条件变量上所有的等待线程

        int  pthread_cond_broadcast(pthread_cond_t  *cond);//唤醒条件变量上的某个线程

        返回值:成功返回0,失败返回错误码;

      

        实例查看: 多线程的生产者和消费者问题

        转载地址:http://blog.csdn.net/yuzhihui_no1/article/details/46523251

 

0 0
原创粉丝点击