首页 > 编程知识 正文

linux多线程共享变量,linux多线程是怎么实现的

时间:2023-05-04 08:53:05 阅读:233844 作者:2555

上一节中,Linux多线程之互斥锁最后遗留了一个问题,consumewait函数会一直轮询检查生产者是否生产好了条目,这样很浪费CPU的时间,因此,需要有另外一种类型的同步,它允许一个线程(或进程)睡眠到发生某个事件为止。

互斥锁用于上锁,条件变量则用于等待。这两种不同类型的同步都是需要的。条件变量是类型为pthread_cond_t的变量,以下两个函数使用了这些

#include <pthread.h>int pthread_cond_wait( pthread_cond_t *cptr, pthread_mutex_t *mptr);int pthread_cond_signal( pthread_cond_t *cptr)每个条件变量总是有一个互斥锁与之关联。我们调用pthread_cond_wait等待某个条件为真时,还会指定其条件变量的地址和所关联的互斥锁的地址。

我们通过编写上一节中的例子来解释条件变量的使用。

#define MAXNITEMS 100000#define MAXNTHREADS 100int nitems ;int buffer[MAXNITEMS];struct{ pthread_mutex_t mutex ; int nput; int nval;} put = { PTHREAD_MUTEX_INITIALIZER };struct{ pthread_mutex_t mutex ; pthread_cond_t cond ; int nready ;//number ready for consumer} ready = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER };

把互斥锁变量mutex以及与之关联的两个变量nput和nval收集到一个名为put的结构中,生产者使用这个结构。

把互斥锁、条件变量以及计数器收集到一个名为ready的结构中,消费者使用这个结构。

客户端代码没有变动,produce和consume函数变动了。当生产者往数组buff放置一个新条目时,我们改用互斥锁put.mutex来为临界区上锁。在生产者生产完成之后,给用来统计准备好由消费者处理的条目数的计时器ready.nready加1。在加1之前,如果该计数器的值为0,那就调用pthread_cond_signal唤醒可能正在等待其值变为非零的任意线程。

void * produce( void* arg ){ for( ; ; ) { pthread_mutex_lock( &put.mutex ); if( put.nput > nitems ) { pthread_mutex_unlock(&put.mutex );//array is full, return; return NULL ; } buffer[put.nput] = put.nval; put.nput++; put.nval++; pthread_mutex_unlock( &put.mutex ); pthread_mutex_lock( &ready.mutex ); if( ready.nready == 0 ) { pthread_cond_signal( &ready.cond ); } ready.nready ++ ; pthread_mutex_unlock( &nready.mutex ); * ( (int*)arg ) += 1; }}

消费者只是等待计数器ready.nready变为非零,既然该计数器是在所有的生产者和消费者之间共享的,那么只有锁住与之关联的互斥锁时才能测试它的值。如果在锁住该互斥锁期间计数器的值为0,我们就调用pthread_cond_wait进入睡眠。该函数原子地执行以下两个动作:

给互斥锁ready.mutex解锁;把调用线程投入睡眠,直到另外某个线程就本身条件变量调用pthread_cond_signal。void *comsume( void* arg ){ int i ; for( i =0 ;i < nitems; i ++ ) { pthread_mutex_lock( &ready.mutex ); while( ready.nready == 0 ) { pthread_cond_wait( &ready.cond, &ready.mutex ); } ready.nready -- ; pthread_mutex_unlock( &ready.mutex ); printf("buffer[%d] = %dn", i, buffer[i] ); } return NULL ;}

之前的produce函数可能会引起上锁冲突,pthread_cond_signal由当前锁住某个互斥锁的线程调用,而该互斥锁是与本函数将给它发送信号的条件变量关联的。我们可以假想最坏情况,当该条件变量被发送信号后,系统立即调度等待其上的线程,该线程开始运行,但立即停止,因为未解锁。可把程序改成如下:

void * produce( void* arg ){ for( ; ; ) { pthread_mutex_lock( &put.mutex ); if( put.nput > nitems ) { pthread_mutex_unlock(&put.mutex );//array is full, return; return NULL ; } buffer[put.nput] = put.nval; put.nput++; put.nval++; pthread_mutex_unlock( &put.mutex ); int dosignal = 0; pthread_mutex_lock( &ready.mutex ); dosignal = ( ready.nready == 0 ); ready.nready ++ ; pthread_mutex_unlock( &nready.mutex ); if( dosignal ) { pthread_cond_signal( &ready.cond ); } * ( (int*)arg ) += 1; }}

pthread_cond_signal只能唤醒等待在相应条件变量上的一个线程,在某些情况下一个线程认定有多个其它线程应被唤醒,这时可调用pthread_cond_broadcast唤醒阻塞在相应条件变量上的所有线程。坚持使用广播,拒绝不知情的情况下使用单播。


pthread_cond_timedwait允许线程就阻塞时间设置一个限制值,abstime参数就是一个timespec结构。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。