互斥量从本质上说就像是一把锁,提供资源的保护访问,互斥量有两种状态,锁住(lock)与解锁状态(unlock),用来保证一段时间内只有一个线程使用该共享资源。
互斥量的数据类型为pthread_mutex_t,如果互斥锁变量是静态分配的,那么一般将其初始化为常值PTHREAD_MUTEX_INITIALIZER, 如:static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
r若互斥锁变量是动态分配的(如调用malloc函数)或者说是该互斥锁分配在共享内存区中,则需要用初始化函数
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
来初始化该互斥锁变量。
互斥锁保护的是临界区,但实际上其保护的临界区中的被操纵的数据,即就是说,互斥锁通常用于保护由多个线程或多个进程分享的共享数据。
互斥锁的上锁与解锁函数:
int pthread_mutex_lock(pthread_mutex_t *mutex);
该函数是互斥锁的上锁(加锁)函数,成功返回0,失败返回错误编号,使用该函数时如果一个互斥锁已经被某个线程加锁锁住,调用该函数就会阻塞调用线程直到该互斥锁解锁为止。参数mutex是互斥锁变量。
int pthread_mutex_trylock(pthread_mutex_t *mutex);
该函数是互斥锁的尝试上锁函数,成功返回0,失败返回错误编号。该函数是一个非阻塞函数(就是不阻塞),即就是若一个互斥锁变量已经已经被别的线程加锁,则调用该函数的线程尝试加锁,则其加锁不成功,但是其不会阻塞该线程,而是继续向下执行。若一个互斥锁未被加锁,则其就会加锁成功。
参数mutex是互斥锁变量。
int pthread_mutex_unlock(pthread_mutex_t *mutex);
该函数是互斥锁的解锁函数,成功返回0,失败返回错误编号。只要临界区的操作执行完成,就解锁互斥量。参数mutex是互斥锁变量。
互斥锁变量的初始化和销毁函数:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
该函数是互斥量的销毁函数,参数为互斥量。成功返回0.失败返回错误编号。
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
该函数是互斥量的初始化函数,mutex参数为互斥量,attr为互斥量的属性。成功返回0,失败返回错误编号。该函数用于动态分配或是在共享内存区定义的互斥量的初始化。
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
这是互斥量的静态初始化方法,用于静态初始化的互斥量。
以下是一个使用互斥量同步的生产者和消费者程序:
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#define BUFFER_SIZE 6
#define MAX_SIZE 50
struct{
pthread_mutex_t mutex;
pthread_cond_t nofull;
pthread_cond_t noempty;
int buff[MAX_SIZE];
int nput; //存放数据的缓冲区下标
int nval; //存放的数据值
int count; //缓冲区值中的存放的数据个数
}shared = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER};
//生产者程序
void *produce(void *arg)
{
for( ; ; ){
pthread_mutex_lock(&shared.mutex); //加锁从此行至pthread_mutex_unlock形成临界区
if(shared.nval >= MAX_SIZE){ //若生产者产生的数据值>=最大值就解锁退出
pthread_mutex_unlock(&shared.mutex);
printf("hjhfrkfhrkjfhrkfr\n");
break;
}
if(shared.count < BUFFER_SIZE){ //若缓冲区中存放的数据个数小于缓冲区所能存放的个数
shared.buff[shared.nput] = shared.nval; //就继续在缓冲区中存入数据
shared.nput++; //改变缓冲区中存放的数据的下标
if(shared.nput >= BUFFER_SIZE){ //当缓冲区的数据的下标>=缓冲的大小时,又开始重新计数
shared.nput = 0;
}
shared.nval++; //生产者产生的数据值增加
shared.count++; //生产者产生的数据个数增加
pthread_cond_signal(&shared.noempty); //给消费者线程发送信号,告诉消费者线程缓冲区中有数据可读
}else{ //缓冲区空间不足,无法存入数据,使生产者线程处于阻塞状态
pthread_cond_wait(&shared.nofull, &shared.mutex); //阻塞生产者线程,等待消费者线程给其发送缓冲区不满的信号
}
pthread_mutex_unlock(&shared.mutex); //解锁互斥量
}
}
//消费者程序
void *consume(void *arg)
{
int j = 0; //缓冲区的计数器,用来读出缓冲区中的数据
//遍历输出读出的数据
for(int i = 0; i < MAX_SIZE; ){
pthread_mutex_lock(&shared.mutex); //加锁互斥量至pthread_mutex_unlock形成临界区
if(shared.count > 0){ //若缓冲区中的数据个数>0就将其中的数据读出
printf("value = %d\n", shared.buff[j]);
//sleep(1);
j++; //缓冲区的数据下标,后移读出下一个位置的数据
if(j >= BUFFER_SIZE){ //若该计数下标>=缓冲区的大小,就重新从下标0开始重新计数
j = 0;
}
shared.count--; //缓冲区中的元素个数减一
pthread_cond_signal(&shared.nofull); //给生产者线程发送信号,告诉生产者线程缓冲区不满,可以存入数据
i++; //计数器,用来计数该读出的数据的个数
}else{ //缓冲区中没有数据可以读出,就阻塞等待缓冲区中填入数据
pthread_cond_wait(&shared.noempty, &shared.mutex); //阻塞等待生产者线程发送缓冲区不空的信号
}
pthread_mutex_unlock(&shared.mutex); //解锁互斥锁
}
}
int main()
{
pthread_t ptid;
pthread_t ctid;
pthread_create(&ptid, NULL, produce, NULL); //创建生产者线程
pthread_create(&ctid, NULL, consume, NULL); //创建消费者线程
pthread_join(ptid, NULL); //终止生产者线程
pthread_join(ctid, NULL); //终止消费者线程
return 0;
}