互斥锁和条件变量是出自Posix线程标准,用来同步一个进程中各个线程的,同时也可以用来同步几个进程间的,不过这需要此互斥锁和条件变量是存放在多个进程间共享的某个内存区的。
互斥锁上锁与解锁:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
互斥锁初始化的问题:
可以通过两种方式初始化一个互斥锁变量:
1,如果互斥锁变量是静态分配的,那么使用如下形式初始化:
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
2,如果互斥锁是动态分配的,那么我么可以用pthread_mutex_init函数初始化它。
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
这两者有个很关键的地方,静态分配的话,只能使用默认的属性,而对于动态分配的,我们可以设置属性。
条件变量:等待与信号发送:
互斥锁有个劣势,那就是他仅仅是上锁和解锁,效率低,这时我们可以通过引入条件变量来解决问题,它允许一个线程或进程睡眠到某个事件为止。
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
每一个条件变量总是有一个互斥锁与之关联,我们再等待某个条件为真时,还会指定条件变量的地址和所关联的互斥锁的地址,这是什么意思呢?就是说,在使用pthread_cond_wait函数之前,我们要先用一个互斥锁锁住,然后当我们调用pthread_cond_wait函数进入睡眠。注意:该函数原子的执行两个动作:
1,给互斥锁解锁。(这就要求在调用这个函数之前要上锁)
2,把调用线程投入睡眠。
一旦这个函数被唤醒后,那么在此函数返回前重新给互斥锁上锁。这也就决定了在接下来的程序里必须有解锁的步骤。
互斥锁和条件变量的属性:
我们可以通过设置属性来选择是一个进程中多个线程同步还是多个进程间的同步。
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
互斥锁和条件变量的属性如同互斥锁和条件变量一样,也分为静态和动态分配及其初始化。
#include <pthread.h>
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_condattr_destroy(pthread_condattr_t *attr);
int pthread_condattr_init(pthread_condattr_t *attr);
至于前面讲到的更改互斥锁和条件变量的属性来达到切换是线程间同步还是进程间同步的问题是通过如下函数来操作的:
#include <pthread.h>
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *
restrict attr, int *restrict pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,
int pshared);
int pthread_condattr_getpshared(const pthread_condattr_t *restrict attr,
int *restrict pshared);
int pthread_condattr_setpshared(pthread_condattr_t *attr,
int pshared);
pshared的值就是用来设置这个属性的值,它可以是PTHREAD_PROCESS_PRIVATE(线程间同步)或PTHREAD_PROCESS_SHARED(进程间同步)。
条件变量定时等待和广播:
用来唤醒阻塞在此条件变量上的所有线程。
1 #include <pthread.h>
2
3 int pthread_cond_broadcast(pthread_cond_t *cond);
4 int pthread_cond_timedwait(pthread_cond_t *restrict cond,
5 pthread_mutex_t *restrict mutex,
6 const struct timespec *restrict abstime);
7 int pthread_cond_wait(pthread_cond_t *restrict cond,
8 pthread_mutex_t *restrict mutex);
这里要说明一点,对于第二个函数所提到的abstime,这是绝对时间,而不是我们一般索说到的相差时间,也就是说这个时间是指自UTC时间以来所流逝的秒数和纳秒数,这样就有个一个好处:如果函数因为有信号到来而过早返回了,那么这个函数可以在无需更改参数的情况下继续再次被调用。
小结:
互斥锁用于保护代码临界区,从而保证任何时刻只有一个线程或者进程在临界区执行。有时候一个线程获得某个互斥锁后,发现自己需要等待某个条件变为真,这样线程就可以等待在某个条件上。条件变量总是有一个互斥锁与之关联。
互斥锁和条件变量可以静态分配并静态初始化。它们也可以动态分配并要求动态地初始化它们。动态初始化允许我们指定进程间共享属性,从而允许在不同进程间共享某个互斥锁或条件变量,其前提是该互斥锁或条件变量必须存在在由这些进程共享的内存区。