详解linux互斥锁 pthread_mutex和条件变量pthread_cond
[cpp] view plaincopy ============================================================= int pthread_create( pthread_t *tid, const pthread_attr_t *attr, void*(*start_routine)(void*), void *arg ); //参数tid 用于返回新创建线程的线程号; //start_routine 是线程函数指针,线程从这个函数开始独立地运行; //arg 是传递给线程函数的参数。由于start_routine 是一个指向参数类型为void*,返回值为void*的指针,所以如果需要传递或返回多个参数时,可以使用强制类型转化。 ============================================================= void pthread_exit( void* value_ptr ); // 参数value_ptr 是一个指向返回状态值的指针。 ============================================================= int pthread_join( pthread_t tid , void **status ); // 参数tid 是希望等待的线程的线程号,status 是指向线程返回值的指针,线程的返回值就是pthread_exit 中的value_ptr 参数,或者是return语句中的返回值。该函数可用于线程间的同步。 ============================================================= int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutex_attr_t* attr ); //该函数初始化一个互斥体变量,如果参数attr 为NULL,则互斥 //体变量mutex 使用默认的属性。 ============================================================= int pthread_mutex_lock( pthread_mutex_t *mutex ); // 该函数用来锁住互斥体变量。如果参数mutex 所指的互斥体已经 //被锁住了,那么发出调用的线程将被阻塞直到其他线程对mutex 解锁。 ============================================================= int pthread_mutex_trylock( pthread_t *mutex ); //该函数用来锁住mutex 所指定的互斥体,但不阻塞。如果该互斥 //体已经被上锁,该调用不会阻塞等待,而会返回一个错误代码。 ============================================================= int pthread_mutex_unlock( pthread_mutex_t *mutex ); //该函数用来对一个互斥体解锁。如果当前线程拥有参数mutex 所 //指定的互斥体,该调用将该互斥体解锁。 ============================================================= int pthread_mutex_destroy ( pthread_mutex_t *mutex ); //该函数用来释放分配给参数mutex 的资源。调用成功时返回值为 //0, 否则返回一个非0 的错误代码。 ============================================================= int pthread_cond_init( pthread_cond_t *cond, const pthread_cond_attr_t*attr ); //该函数按参数attr指定的属性创建一个条件变量。调用成功返回, //并将条件变量ID 赋值给参数cond,否则返回错误代码。 ============================================================= int pthread_cond_wait ( pthread_cond_t *cond , pthread_mutex_t*mutex ); // 该函数调用为参数mutex 指定的互斥体解锁,等待一个事件(由 //参数cond 指定的条件变量)发生。调用该函数的线程被阻塞直到有其他 //线程调用pthread_cond_signal 或pthread_cond_broadcast 函数置相应的条 //件变量,而且获得mutex 互斥体时才解除阻塞。 ============================================================= int pthread_cond_timewait( pthread_cond_t *cond , pthread_mutex_t*mutex , const struct timespec *abstime ); // 该函数与pthread_cond_wait 不同的是当系统时间到达abstime 参数指定的时间时,被阻塞线程也可以被唤起继续执行。 ============================================================= int pthread_cond_broadcast( pthread_cond_t *cond ); // 该函数用来对所有等待参数cond所指定的条件变量的线程解除阻塞,调用成功返回0,否则返回错误代码。 ============================================================= int pthread_cond_signal( pthread_cond_t *cond ); // 该函数的作用是解除一个等待参数cond所指定的条件变量的线程的阻塞状态。当有多个线程挂起等待该条件变量,也只唤醒一个线程。 ============================================================= int pthread_cond_destroy( pthread_cond_t *cond ); // 该函数的作用是释放一个条件变量。释放为条件变量cond 所分配的资源。调用成功返回值为0,否则返回错误代码。 ============================================================= int pthread_key_create( pthread_key_t key , void(*destructor(void*)) ); // 该函数创建一个键值,该键值映射到一个专有数据结构体上。如果第二个参数不是NULL,这个键值被删除时将调用这个函数指针来释放数据空间。 ============================================================= int pthread_key_delete( pthread_key_t *key ); // 该函数用于删除一个由pthread_key_create 函数调用创建的TSD键。调用成功返回值为0,否则返回错误代码。 ============================================================= int pthread_setspecific( pthread_key_t key , const void(value) ); // 该函数设置一个线程专有数据的值,赋给由pthread_key_create 创建的TSD 键,调用成功返回值为0,否则返回错误代码。 ============================================================= void *pthread_getspecific( pthread_key_t *key ); // 该函数获得绑定到指定TSD 键上的值。调用成功,返回给定参数key 所对应的数据。如果没有数据连接到该TSD 键,则返回NULL。 ============================================================= int pthread_once( pthread_once_t* once_control, void(*init_routine)(void) ); //该函数的作用是确保init_routine 指向的函数,在调用pthread_once的线程中只被运行一次。once_control 指向一个静态或全局的变量。 ============================================================= 在code review中,我会发现很多人喜欢在pthread_mutex_lock()和pthread_mutex_unlock(()之间调用 pthread_cond_signal或者pthread_cond_broadcast函数,从逻辑上来说,这种使用方法是完全正确的。但是在多线程 环境中,这种使用方法可能是低效的。posix1标准说,pthread_cond_signal与pthread_cond_broadcast无需考 虑调用线程是否是mutex的拥有者,也就是所,可以在lock与unlock以外的区域调用。如果我们对调用行为不关心,那么请在lock区域之外调用 吧。这里举个例子: 我们假设系统中有线程1和线程2,他们都想获取mutex后处理共享数据,再释放mutex。请看这种序列: 1)线程1获取mutex,在进行数据处理的时候,线程2也想获取mutex,但是此时被线程1所占用,线程2进入休眠,等待mutex被释放。 2)线程1做完数据处理后,调用pthread_cond_signal()唤醒等待队列中某个线程,在本例中也就是线程2。线程1在调用 pthread_mutex_unlock()前,因为系统调度的原因,线程2获取使用CPU的权利,那么它就想要开始处理数据,但是在开始处理之 前,mutex必须被获取,很遗憾,线程1正在使用mutex,所以线程2被迫再次进入休眠。 3)然后就是线程1执行pthread_mutex_unlock()后,线程2方能被再次唤醒。 从这里看,使用的效率是比较低的,如果再多线程环境中,这种情况频繁发生的话,是一件比较痛苦的事情。 所以觉得,如果程序不关心线程可预知的调度 行为,那么最好在锁定区域以外调用他们吧:-) 如果读者喜欢英文的话,可以参考: http://www.opengroup.org/onlinepubs/007908799/xsh/pthread_cond_broadcast.html 这 里罗嗦几句,对于 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 *abstime); ,一定要在mutex的锁定区域内使用。 如果要正确的使用pthread_mutex_lock与pthread_mutex_unlock,请参考 pthread_cleanup_push 和pthread_cleanup_pop宏,它能够在线程被cancel的时候正确的释放mutex! http://blog.csdn.net/hello_wyq/archive/2006/08/23/1108264.aspx PTHREAD_COND(3) 名称 pthread_cond_init, pthread_cond_destroy, pthread_cond_signal, pthread_cond_broadcast, pthread_cond_wait, pthread_cond_timedwait - 状态操作。 大纲 #include pthread_cond_t cond = PTHREAD_COND_INITIALIZER; int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr); int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); 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 *abstime); int pthread_cond_destroy(pthread_cond_t *cond); 描述 状态变量是一种同步设备,它允许线程挂起并放弃CPU时间以等待某些共享变量满足状态。操作状态变量的基本操作:当状态满足时发送信号,等待状态满足,在其它线程发送状态信号之前挂起线程。 状态变量通常和互斥体联系在一起,为了避免竞争状态出现,一个线程准备等待一个状态变量之前另外一个线程要先设置好状态信号。 pthread_cond_init初始化状态变量cond,使用cond_attr中定义了的状态属性,如果cond_attr为NULL,将会使用默认属性。LinuxThreads的实现支持没有任何属性的cond_attr,因此,cond_attr就是被忽略的。 pthread_cond_t类型的变量也可以使用PTHREAD_COND_INITIALIZER.常量进行静态的初始化。 pthread_cond_signal函数重新开始一个正在等待cond变量的线程。如果没有线程在等待cond变量,不执行任何操作。如果有多个线程都在等待,某个匹配的线程会被重新开始,但是不一定是哪个。 pthread_cond_broadcast函数重新开始所有在等待cond变量的线程。如果没有线程在等待cond变量,不执行任何操作。 pthread_cond_wait函数对互斥体进行原子的解锁工作(就像pthread_unlock_mutex),然后等待状态信号。线程被挂起并不消耗CPU时间,直到发送状态信号。互斥体必须被调用者锁定。在返回调用线程之前,互斥锁被pthread_cond_wait拥有。 释放互斥体和在状态变量上挂起是自动进行的。因此,如果所有的线程经常在状态信号之前要求互斥体,这会保证在线程在状态变量上锁定互斥体的期间状态变量不会触发信号。 pthread_cond_timedwait函数自动释放互斥体并且等待cond状态,就像pthread_cond_wait所做的一样,但是它限制了最大的等待时间。如果cond没有在一定时间内用abstime中没有指定的时间做标记,互斥体会重新获得,然后返回错误码ETIMEOUT。abstime参数指定绝对时间,和time(2)和gettimeofday(2)的一样:以格林威治时间1970年1月1日零点为起点。 pthread_cond_destroy函数销毁状态变量,释放它可能持有的资源。没有线程必须在这里等待状态变量。在LinuxThreads实现中,状态变量不与任何资源有关系,所以这个接口除了检查状态变量上是否有等待的线程之外不做任何事。 取消 pthread_cond_wait和pthread_cond_timedwait都是取消点。如果某个线程在某个这样的函数中挂起后又被取消,该线程会重新开始执行,然后再锁定互斥体。(尚未完成!) 同步信号安全 状态函数不是同步安全的,不应该出现在信号处理函数中。特别的,从信号处理函数中调用pthread_cond_signal或pthread_cond_broadcast会导致死锁。 返回值 所有状态变量函数在成功的时候返回0,在出错的时候返回出错码。 错误码 pthread_cond_init, pthread_cond_signal, pthread_cond_broadcast和pthread_cond_wait从来都不会返回出错码。 pthread_cond_timedwait函数在出错时返回下面的出错码: ETIMEOUT:状态变量在abstime限定时间内没有激发信号。 EINTR:pthread_cond_timedwait被信号中断。 pthread_cond_destroy函数在出错时返回下面的出错码: EBUSY:有线程正在cond上等待。 作者 Xavier Leroy 参见 pthread_condattr_init(3), pthread_mutex_lock(3), pthread_mutex_unlock(3) gettimeofday(2), nanosleep(2). 示例 考虑两个共享变量x和y,使用互斥体mut进行保护,一个状态变量cond在x大于y时激发信号。 int x,y; pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 一直等待,直到x大于y: pthread_mutex_lock(&mut); while (x <= y) { pthread_cond_wait(&cond, &mut); } /* operate on x and y */ pthread_mutex_unlock(&mut); 修改x和y可能会使x大于y。如果需要就应该发送信号: pthread_mutex_lock(&mut); /* modify x and y */ if (x > y) pthread_cond_broadcast(&cond); pthread_mutex_unlock(&mut); 如果可以证明,至多有一个等待中的线程需要醒来(例如,只有两个线程,通过x和y联系起来),pthread_cond_signal可以作为可选的更有效率的轻量级的pthread_cond_broadcast.。如果不能确定,使用pthread_cond_broadcast函数。 要在5秒钟内等待x大于y,像下面: struct timeval now; struct timespec timeout; int retcode; pthread_mutex_lock(&mut); gettimeofday(&now); timeout.tv_sec = now.tv_sec + 5; timeout.tv_nsec = now.tv_usec * 1000; retcode = 0; while (x <= y && retcode != ETIMEDOUT) { retcode = pthread_cond_timedwait(&cond, &mut, &timeout); } if (retcode == ETIMEDOUT) { /* timeout occurred */ } else { /* operate on x and y */ } pthread_mutex_unlock(&mut);
posted on 2015-11-20 17:19 zyz913614263 阅读(5954) 评论(0) 编辑 收藏 举报