互斥量
互斥量的概念
由于在大多数机器中,对变量的增量和减量操作都不是原子的。比如通常情况下,增量操作包括三个步骤:将内存中的数值装载到CPU寄存器中,将寄存器的值加1,将寄存器中的值写回内存。而机器并不保证在这三步之间不会发生调度,这就导致对变量的增量和减量操作有可能得不到期望的结果。比如,当某线程正在进行增量操作,寄存器中的数值被加1之后发生了线程切换,另一个线程对同一个变量进行了增量操作后又切换回原线程,原线程将寄存器中的值写回内存。此时,变量只被增加了1,而不是增加了2。 要解决这个问题,就要保证对变量的增量和减量操作是原子的,也就是说,在操作过程中不允许发生线程切换。这就用到了互斥量。
互斥量是一种特殊的变量,它可以处于锁定状态(locked),也可以处于解锁状态(unlocked)状态。如果互斥量是锁定的,那么必然有一个线程持有或拥有这个互斥量。如果没有任何一个线程持有这个互斥量,那么这个互斥量就处于解锁、空闲或可用状态。当互斥量空闲,并且有一个线程试图获取这个互斥量时,这个线程就可以获得这个互斥量而不会被阻塞。如果互斥量处于锁定状态,那么试图获取这个互斥量的线程将被阻塞,并加入到这个互斥量的等待队列中。等待队列中的线程获得互斥量的顺序由实现系统决定。这样的机制解决了共享资源的互斥(Mutual Exclusive)访问问题。
创建并初始化一个互斥量
POSIX使用pthread_mutex_t类型的变量来表示互斥量。程序在使用pthread_mutex_t变量之前,必须对其进行初始化。对于静态分配的pthread_mutex_t变量,只要将PTHREAD_MUTEX_INITIALIZER赋给这个变量即可,pthread_mutex_t g_tMutex = PTHREAD_MUTEX_INITIALIZER;
对于动态创建或不使用默认属性的互斥量来说,就要调用pthread_mutex_init函数来对其进行初始化。函数形式为:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
参数mutex是指向要初始化的互斥量的指针,参数attr是设定互斥量属性的变量的指针,如果为NULL,则使互斥量拥有默认属性。
销毁一个互斥量
当不再使用已经定义了的互斥量时,需要将互斥量销毁。函数pthread_mutex_destory用于销毁互斥量。它的形式为:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数mutex指向要销毁的互斥量。如果成功,函数返回0,如果不成功,函数返回一个非零的错误码。
可以用pthread_mutex_init重新初始化被销毁的互斥量。
互斥量的锁定和解锁
POSIX中有两个可以用来获取互斥量的函数,pthread_mutex_lock和pthread_mutex_trylock。pthread_mutex_lock函数会使调用这个函数的线程一直阻塞到互斥量可用为止,而pthread_mutex_trylock会立即返回,如果互斥量空闲,那么调用这个函数的线程将获得互斥量,否则函数将返回EBUSY。pthread_mutex_unlock用于释放互斥量。这三个函数的形式为:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
如果成功,这些函数就返回0,如果不成功,这些函数就返回一个非零的错误码。
互斥量应用实例
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/types.h>
#include <dirent.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <time.h>