目录
在多线程(并发)程序中,多个线程会访问同一个共享内存,为了避免产生一些奇怪的结果,这些线程应该按照合适的顺序访问共享内存,这个过程称为同步(synchronization)。可是仅仅保持同步还不够,为了让同步更加高效,多个线程互相之间也要保持交流。这篇文章主要讨论线程同步(synchronization)和交流(communication)。
1. Critical Section(CS)
临界区(Critical section)是程序的一段代码块,临界区不能被多个线程在同一时间访问,临界区的访问是互斥的,某一时刻最多只能有一个线程能进临界区。
2. Lock
Lock,叫做锁,提供了一种互斥的方式,Lock能保证某一时刻只有一个线程能进入到临界区(CS),如下面的伪代码:
Lock num_mutex; // num_mutex 是一个Lock对象
######################################
Thread1:
// 获取锁
mutex_lock(&num_mutex);
// ******** 临界区 *******
num++;
// ******** 临界区 *******
// 释放锁
mutex_unlock(&num_mutex);
######################################
Thread2:
// 获取锁
mutex_lock(&num_mutex);
// ******** 临界区 *******
num++;
// ******** 临界区 *******
// 释放锁
mutex_unlock(&num_mutex);
在上面的伪代码中,num++ 不是原子性操作,所以 num++ 不能同时被两个线程执行。 当Thread1和Thread2同时获取锁,只有一个线程最终能获取锁,然后获取到锁的线程进入临界区(执行num++),另一个线程必须一直等待到锁被释放。
再来看下经典的生产者-消费者问题,用Lock可以解决这个问题,如下代码:
Lock count_mutex; // count_mutex 是一个Lock对象
int count = 0; // 待消费的商品数量
#############################################
Producer Thread
// 获取锁
mutex_lock(&count_mutex);
// 如果count等于 MAX_SIZE, 不能生产,需要等待
while(count == MAX_SIZE){
// 释放锁,将锁让给Consumer
mutex_unlock(&count_mutex);
Thread.sleep(3000); // 睡眠3秒
// 睡眠唤醒之后,再次获取锁
mutex_lock(&count_mutex);
}
// ***** 临界区 ****//
// 生产
produce();
// 生产之后,count加1
count++;
// ***** 临界区 ****//
// 生产之后释放锁
mutex_unlock(&count_mutex);
##############################################
Consume