一、互斥量mutex
多线程可以考虑同一时间只有一个线程访问数据。互斥量(mutex)就是一把锁。
多个线程只有一把锁一个钥匙,谁上的锁就只有谁能开锁。当一个线程要访问一个共享变量时,先用锁把变量锁住,然后再操作,操作完了之后再释放掉锁,完成。
当另一个线程也要访问这个变量时,发现这个变量被锁住了,无法访问,它就会一直等待,直到锁没了,它再给这个变量上个锁,然后使用,使用完了释放锁,以此进行。
这个即使有多个线程同时访问这个变量,也好象是对这个变量的操作是顺序进行的。
互斥变量使用特定的数据类型:pthread_mutex_t,使用互斥量前要先初始化,
pthread_mutex_t
pthread_mutex_t 类型,其本质是一个结构体,为简化理解,应用时可忽略其实现细节,简单当成整数看待。
pthread_mutex_t mutex:变量mutex只有两种取值0、1;
互斥量初始化:
a)、POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。
b)、动态方式是采用pthread_mutex_init()函数来初始化互斥锁,API定义如下:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
其中mutexattr用于指定互斥锁属性(见下),如果为NULL则使用缺省属性。
加锁与解锁
lock与unlock:
lock尝试加锁,如果加锁不成功,线程阻塞,阻塞到持有该互斥量的其他线程解锁为止。
unlock主动解锁,同时将阻塞到该锁上所有线程全部唤醒,至于哪个线程先被唤醒取决于优先级,调度。默认:先阻塞、先唤醒。
例如:T1 T2 T3 T4 使用一把mutex锁,T1加锁成功,其他线程均阻塞,直至T1解锁,T1解锁后,T2 T3 T4均被唤醒,并自动再次尝试加锁。
避免死锁
产生死锁的情况较多,如一个线程对变量a加锁后,试图对变量b加锁,另一个线程对变量b加了锁,试图对a加锁,这时两个线程都不释放锁,加不会加锁成功,造成两个线程处于死锁状态。
可以在设计中避免死锁的发生。如使用 pthread_mutex_timedlock函数,该函数允许线程阻塞特定时间,如果加锁失败就会返回ETIMEDOUT。函数原型如下:
#include <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timesec *restrict tsptr);
锁操作:
pthread_mutex_destroy ()注销一个互斥锁
加锁 pthread_mutex_lock()、
解锁pthread_mutex_unlock()
测试加锁 pthread_mutex_trylock()三个
等待和激发:
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)
等待条件有两种方式:条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),
其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()、pthread_cond_timedwait()的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(thread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
激发条件有两种形式,
pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;
而pthread_cond_broadcast()则激活所有等待线程。
3、处理流程
pthread_cond_wait使用前必须由本线程调用pthread_mutex_lock加锁
pthread_mutex_lock(&mtx);
pthread_cond_wait(&cond,&mtx);
pthread_mutex_unlock(&mtx);
pthread_cond_wait内部处理流程:解锁—>阻塞休眠—>唤醒—>加锁