互斥锁与条件变量简介
在多线程的环境中,全局变量会被各线程共享,因此在操作全局变量的时候需要采用锁机制,在linux里最常用的锁就是互斥锁,互斥锁使用方法如下
<pre name="code" class="cpp">//线程A
pthread_mutex_lock(&lock);
...; //对共享数据操作
pthread_mutex_unlock(&lock);
这段代码会包括在每一个试图操作共享变量的线程里面,这样就能实现共享变量(临界资源)的互斥使用。
这样也会遇见一个问题,每个线程在临界资源释放之后都会争夺锁,获得锁的线程会继续执行下去,其他线程都将阻塞在原地直到临界资源被释放后重新争夺锁,这样很容易造成有的线程一直使用锁而有的线程一直抢不到锁,从而造成饿死的现象使程序运行效率十分低下。
为了解决这个问题linux提供了条件变量来使线程之间同步。
条件变量的使用方法如下
<pre name="code" class="cpp">//线程A
pthread_mutex_lock(&lock);
if(超前)
pthread_cond_wait(&不超前,&lock);
...; //对共享数据操作
pthread_mutex_unlock(&lock);
//线程B
pthread_mutex_lock(&lock);
if(A不超前)
pthread_cond_signal(不超前);
...; //对共享数据操作
pthread_mutex_unlock(&lock);
因为条件变量的等待涉及到解锁和加锁操作,所以都将它放在锁内执行。许多人对条件变量的使用理解的不是很正确,在执行wait操作的过程中先执行了解锁操作,然后阻塞在该条件变量上面,直到某个线程发送信号来激活阻塞的线程。注意:当被阻塞的线程被激活之后它并没有立即往下面执行,而是等待占有锁的线程释放锁,然后再和其他线程共同去争抢锁的使用权,获得锁之后才可以继续往下执行。
有了以上的理解我们来写一段代码。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#define BUFFER_SIZE 512
pthread_mutex_t lock; //互斥锁
pthread_cond_t notempty;
pthread_cond_t notfull;
int buf[BUFFER_SIZE];
int writepos, readpos;
int writenum = 0;
int readnum = 0;
int exit_condition = 0;
//变量初始化
void init()
{
pthread_mutex_init(&lock,NULL);
pthread_cond_init(¬empty,NULL);
pthread_cond_init(¬full,NULL);
writepos = 0;
readpos = 0;
}
//写操作
void put(int data)
{
//这个mutex_lock主要是用来保护wait等待临界时期的情况
pthread_mutex_lock(&lock);
while((writepos+1)%BUFFER_SIZE == readpos)
{
pthread_cond_wait(¬full,&lock);
}
writenum++;
buf[writepos] = data;
writepos++;
if(writepos>=BUFFER_SIZE)
writepos=0; //缓冲区满,将回到原点
pthread_cond_signal(¬empty);
pthread_mutex_unlock(&lock);
}
//读操作
int get()
{
int data;
pthread_mutex_lock(&lock);
while(writepos == readpos)
{
pthread_cond_wait(¬empty,&lock);
}
readnum++;
data = buf[readpos];//从缓冲区读数据
readpos++;
if(readpos>=BUFFER_SIZE)
readpos=0;
pthread_cond_signal(¬full);
pthread_mutex_unlock(&lock);
return data;
}
void* producer(void *data)
{
int i, j;
for(i = 0; i < 1000; i++){
for(j = i; j < 1000; j++)
put(j);
}
exit_condition = 1;
return NULL;
}
void *consumer(void *data)
{
int local_data;
while(1) {
local_data = get();
if(exit_condition == 1 && readnum == writenum)
break;
printf("%d ", local_data);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t produce_id, consumer_id;
init();
pthread_create(&produce_id, NULL, producer, NULL);
pthread_create(&consumer_id, NULL, consumer, NULL);
pthread_join(produce_id,NULL);
pthread_join(consumer_id, NULL);
return 0;
}
这是一个经典的生产者和消费者的例子,线程producer产生数据写在buf循环缓冲区,线程consumer将buf缓冲区的数据取出来并打印,循环缓冲区节省了空间,多线程提高了效率。这段代码可应用在许多场景之中。
希望以上的东西对您有用。