在现代计算领域,多线程编程是提高程序效率和响应性的关键技术之一。本文将深入浅出地介绍线程的基本概念、线程同步机制,特别是互斥锁和条件变量,并通过实现一个经典的“生产者消费者”问题示例来展示这些概念的实际应用
线程基础
线程定义
线程是操作系统调度的最小单位,是进程内的一个执行路径。每个线程拥有独立的栈空间、程序计数器等资源,但共享所属进程的地址空间、全局变量等资源。
线程调度
操作系统通过调度器管理线程的执行,为每个线程分配时间片。时间片的长度直接影响系统的响应性和吞吐量。
线程同步
多线程环境下,为了防止数据竞争和死锁,需要引入同步机制。主要包括互斥锁、信号量、条件变量等。
互斥锁与条件变量
互斥锁
互斥锁(Mutex)用于保护临界区资源,确保同一时刻只有一个线程可以访问共享资源。例如,pthread_mutex_lock()
和 pthread_mutex_unlock()
函数分别用于加锁和解锁。
条件变量
条件变量(Condition Variable)用于线程间的同步,当线程需要等待某个条件成立时,可以使用条件变量进入休眠,直到其他线程通知条件满足。主要函数包括 pthread_cond_wait()
和 pthread_cond_signal()
。
生产者消费者问题实例
问题描述
生产者和消费者共享一个固定大小的缓冲区。生产者负责生产数据放入缓冲区,消费者负责消费数据。需解决缓冲区满时生产者的等待以及缓冲区空时消费者的等待问题。
实现代码(C语言,使用POSIX线程库)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define BUFFER_SIZE 10
#define NUM_PRODUCERS 2
#define NUM_CONSUMERS 2
int buffer[BUFFER_SIZE];
int in = 0, out = 0;
pthread_mutex_t mutex;
pthread_cond_t not_full, not_empty;
void *producer(void *arg) {
int data;
for (;;) {
data = rand() % 100;
pthread_mutex_lock(&mutex);
while (in - out == BUFFER_SIZE) {
pthread_cond_wait(¬_full, &mutex); // 缓冲区满,等待
}
buffer[in % BUFFER_SIZE] = data;
printf("Produced: %d\n", data);
in++;
pthread_cond_signal(¬_empty); // 唤醒消费者
pthread_mutex_unlock(&mutex);
usleep(rand() % 1000000); // 模拟生产间隔
}
return NULL;
}
void *consumer(void *arg) {
for (;;) {
pthread_mutex_lock(&mutex);
while (in - out == 0) {
pthread_cond_wait(¬_empty, &mutex); // 缓冲区空,等待
}
int data = buffer[out % BUFFER_SIZE];
printf("Consumed: %d\n", data);
out++;
pthread_cond_signal(¬_full); // 唤醒生产者
pthread_mutex_unlock(&mutex);
usleep(rand() % 1000000); // 模拟消费间隔
}
return NULL;
}
int main() {
pthread_t producers[NUM_PRODUCERS], consumers[NUM_CONSUMERS];
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(¬_full, NULL);
pthread_cond_init(¬_empty, NULL);
for (int i = 0; i < NUM_PRODUCERS; ++i) {
pthread_create(&producers[i], NULL, producer, NULL);
}
for (int i = 0; i < NUM_CONSUMERS; ++i) {
pthread_create(&consumers[i], NULL, consumer, NULL);
}
// 理论上应等待所有线程结束,此处简化处理
sleep(10); // 运行一段时间后手动结束
for (int i = 0; i < NUM_PRODUCERS; ++i) {
pthread_join(producers[i], NULL);
}
for (int i = 0; i < NUM_CONSUMERS; ++i) {
pthread_join(consumers[i], NULL);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(¬_full);
pthread_cond_destroy(¬_empty);
return 0;
}
解释
- 使用
pthread_mutex_lock()
和pthread_mutex_unlock()
来控制对缓冲区的独占访问。 - 当缓冲区满时,生产者调用
pthread_cond_wait()
在not_full
条件变量上等待,直到消费者消费数据后通过pthread_cond_signal()
唤醒。 - 类似地,消费者在缓冲区空时等待在
not_empty
条件变量上,由生产者完成填充后唤醒。