一、为什么要线程同步
当多个控制线程共享相同的内存时,需要每个线程看到一致的数据视图。
- 共享资源,多个线程都可对共享资源操作
- 线程操作共享资源的先后顺序不确定
- 处理器对存储器的操作一般不是原子操作
二、互斥量
<1> 互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量加锁之后,任何视图再次对互斥量加锁的线程都将被阻塞,直到给互斥量加锁的线程对互斥量解锁为止。
<2> 互斥量的初始化
互斥量用 pthread_mutex_t 数据类型表示,在互斥量使用之前,必须进行初始化。初始化由两种方式:
1) 对于静态分配的互斥量,通常把它置为PTHREAD_MUTEX_INITIALIZER
2) 对于动态分配的互斥量
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t* attr);
int pthread_mutex_destroy(pthread_mutex_t* mutex);
<3> 对互斥量加锁/解锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);//加锁的非阻塞版本
int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 注意,要对互斥量解锁,必须由之前对互斥量进行加锁操作的线程来完成。
<4> 简单的例子
/*
一个生产者,一个消费者,生产者生产产品放在临界区,消费者从临界区取出产品进行消费(设定生产产品的总个数为MAX_NUM,临界区的最大容量为MAX)
*/
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
struct msg{
struct msg *next;
int num;
msg(int x):num(x), next(NULL){};
};
struct msg *head = NULL;
int count = 0; // 临界区当前由多少个产品
static int MAX = 5; // 临界区大小
static int MAX_NUM = 20; // 总共要生产的产品数量
int flage = 1; // 标记是否已经生产完毕
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 初始化互斥量
void *produce(void* ptr);
void *consume(void* ptr);
int main()
{
pthread_t tid_produce, tid_consume;
srand(time(NULL));
pthread_create(&tid_produce, NULL, produce, NULL);
pthread_create(&tid_consume, NULL, consume, NULL);
pthread_join(tid_produce, NULL);
pthread_join(tid_produce, NULL);
return 0;
}
void *produce(void* ptr){
struct msg *mp = NULL;
int current = 0; //表示已经生产的个数
while(1){
if(count < MAX && current < MAX_NUM){
mp = (struct msg*)malloc(sizeof(struct msg));
mp->num = rand()%100 + 1;
count += 1;
current += 1;
printf("Produce %d\tnow count = %d\tcurrent = %d\n", mp->num, count, current);
pthread_mutex_lock(&lock);
mp->next = head;
head = mp;
pthread_mutex_unlock(&lock);
}else if(current == MAX_NUM){
flage = 0;
pthread_exit(0);
}
sleep(rand()%5);
}
}
void *consume(void* ptr){
struct msg *mp = NULL;
while(1){
if(head == NULL && !flage){
//表示已经生产了指定数目的产品且已经全部消费完毕
break;
}
pthread_mutex_lock(&lock);
if(head == NULL){
// 表示临界区内的产品已经消消费完毕,但生产者还会继续生产。此时,需要释放锁,使得produce由得到锁的机会
pthread_mutex_unlock(&lock);
sleep(read()%5);
continue;
}
mp = head;
head = mp->next;
count -= 1;
printf("Consume %d\tthen count = %d\n", mp->num, count);
free(mp);
pthread_mutex_unlock(&lock);
sleep(rand()%5);
}
pthread_exit(0);
}