目录
一、Pthreads API概览
线程创建与管理
pthread_create()函数详解:
- 参数:
pthread_t *thread
:指向新创建线程ID的指针,用于接收创建成功的线程标识符。const pthread_attr_t *attr
:指向线程属性对象的指针,可以指定线程的调度策略、优先级、栈大小等属性。若传入NULL,则使用默认属性。void *(*start_routine)(void *)
:线程执行的起始函数,该函数接受一个void指针参数,并返回一个void指针。void *arg
:传递给线程起始函数的参数。
- 返回值:
- 若成功创建线程,返回0;否则返回错误编号(如EINVAL、EAGAIN等)。
- 错误处理:
- 应检查返回值,若非0,则使用
perror()
或strerror()
输出错误信息,或根据错误编号进行相应处理。
- 应检查返回值,若非0,则使用
- 示例:
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, worker_thread, (void *)&data);
if (ret != 0) {
perror("pthread_create failed");
exit(EXIT_FAILURE);
}
pthread_join()与pthread_detach():线程同步与资源回收
- pthread_join():
- 作用:阻塞调用线程,直到指定线程(通过线程ID指定)执行完毕,并获取其返回值(通过
void **retval
参数接收)。 - 用途:确保线程执行结束并回收其资源,或者获取线程的执行结果。
- 示例:
- 作用:阻塞调用线程,直到指定线程(通过线程ID指定)执行完毕,并获取其返回值(通过
void *thread_return_value;
pthread_join(thread_id, &thread_return_value);
pthread_detach():
- 作用:将指定线程标记为分离状态,使得该线程结束后其资源能自动回收,无需调用
pthread_join()
。 - 用途:适用于不关心线程返回值且希望减少资源管理复杂性的场景。
- 示例:
pthread_detach(thread_id);
线程同步机制
互斥量(Mutexes):
- 创建:使用
pthread_mutex_init()
函数,指定互斥量属性(可选)。 - 锁定:使用
pthread_mutex_lock()
函数,阻塞当前线程直至获得互斥量所有权。 - 解锁:使用
pthread_mutex_unlock()
函数,释放互斥量所有权,可能唤醒等待该互斥量的其他线程。 - 属性设置:通过
pthread_mutexattr_init()
、pthread_mutexattr_settype()
等函数调整互斥量的属性,如是否为递归锁、是否为错误检查锁等。 - 销毁:使用
pthread_mutex_destroy()
函数,释放互斥量占用的资源。
条件变量(Condition Variables):
- 原理:条件变量用于线程间的同步,允许线程在某个条件不满足时阻塞自己,当其他线程改变了该条件并通知等待的线程后,被阻塞的线程才能继续执行。
- 使用场景:生产者-消费者模型、工作队列、多线程资源池等。
- 相关函数:
pthread_cond_init()
:初始化条件变量。pthread_cond_wait()
:阻塞当前线程,直到收到信号或超时。pthread_cond_signal()
:唤醒一个等待该条件变量的线程。pthread_cond_broadcast()
:唤醒所有等待该条件变量的线程。pthread_cond_destroy()
:销毁条件变量。
其他同步原语(如读写锁、屏障等)简介
- 读写锁(pthread_rwlock_t):允许多个读线程同时访问资源,但写线程访问时会排斥所有读写线程。适用于读多写少的场景。
- 屏障(pthread_barrier_t):让一组线程在某个点阻塞,直到所有线程都达到该点才同时解除阻塞,常用于多线程间的同步点控制。
线程局部存储(TLS)
pthread_key_create()与pthread_setspecific():创建与访问线程特定数据
- pthread_key_create():创建一个线程特定数据键,用于关联线程特定数据。
- pthread_setspecific():为当前线程在指定键下设置线程特定数据。
- pthread_getspecific():获取当前线程在指定键下的线程特定数据。
- 应用场景:
- 避免全局数据的竞争:将原本全局的数据改为线程局部存储,每个线程有自己的副本,避免了数据竞争。
- 简化资源管理:线程结束时,与线程特定数据关联的清理函数会被自动调用,简化了资源的生命周期管理。
综上所述,Pthreads API提供了丰富的线程创建、管理、同步功能,包括线程创建与管理函数(如pthread_create()
、pthread_join()
、pthread_detach()
)、线程同步原语(如互斥量、条件变量、读写锁等),以及线程局部存储机制,为编写高效、安全的多线程C程序提供了坚实基础。
二、Pthreads实战示例
简单多线程示例
下面的示例创建了3个计算任务线程,每个线程执行一个简单的计算任务并打印结果,最后主线程等待所有子线程完成。
#include <pthread.h>
#include <stdio.h>
void *compute_task(void *arg) {
int task_id = (int)arg;
int result = task_id * task_id;
printf("Task %d computed result: %d\n", task_id, result);
pthread_exit(NULL); // 线程退出
}
int main() {
pthread_t threads[3];
int task_ids[] = {1, 2, 3};
for (int i = 0; i < 3; ++i) {
int rc = pthread_create(&threads[i], NULL, compute_task, (void *)&task_ids[i]);
if (rc) {
perror("pthread_create failed");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < 3; ++i) {
pthread_join(threads[i], NULL);
}
printf("All tasks completed.\n");
return 0;
}
互斥量同步示例
下面的示例实现了一个线程安全计数器,使用互斥量保护对全局计数器的访问,避免数据竞争。
#include <pthread.h>
#include <stdio.h>
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
int global_counter = 0;
void *increment_counter(void *arg) {
int iterations = (int)arg;
for (int i = 0; i < iterations; ++i) {
pthread_mutex_lock(&counter_mutex);
global_counter++;
pthread_mutex_unlock(&counter_mutex);
}
pthread_exit(NULL);
}
int main() {
pthread_t threads[2];
pthread_create(&threads[0], NULL, increment_counter, (void *)100000);
pthread_create(&threads[1], NULL, increment_counter, (void *)100000);
pthread_join(threads[0], NULL);
pthread_join(threads[1], NULL);
printf("Final counter value: %d\n", global_counter);
return 0;
}
条件变量应用示例
下面的示例实现了一个简单的生产者-消费者模型,使用条件变量协调生产者线程与消费者线程之间的协作。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 5
#define NUM_ITEMS 10
pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
int buffer[BUFFER_SIZE];
int in = 0, out = 0, count = 0;
void *producer(void *arg) {
int item;
for (int i = 0; i < NUM_ITEMS; ++i) {
item = rand() % 100; // 生成随机数作为生产物品
pthread_mutex_lock(&buffer_mutex);
while (count == BUFFER_SIZE) { // 当缓冲区满时,生产者等待
pthread_cond_wait(¬_full, &buffer_mutex);
}
buffer[in++] = item;
in %= BUFFER_SIZE;
count++;
pthread_cond_signal(¬_empty); // 生产物品后,唤醒消费者
pthread_mutex_unlock(&buffer_mutex);
}
pthread_exit(NULL);
}
void *consumer(void *arg) {
int item;
for (int i = 0; i < NUM_ITEMS; ++i) {
pthread_mutex_lock(&buffer_mutex);
while (count == 0) { // 当缓冲区空时,消费者等待
pthread_cond_wait(¬_empty, &buffer_mutex);
}
item = buffer[out++];
out %= BUFFER_SIZE;
count--;
pthread_cond_signal(¬_full); // 消费物品后,唤醒生产者
pthread_mutex_unlock(&buffer_mutex);
printf("Consumed item: %d\n", item);
}
pthread_exit(NULL);
}
int main() {
pthread_t producer_thread, consumer_thread;
pthread_create(&producer_thread, NULL, producer, NULL);
pthread_create(&consumer_thread, NULL, consumer, NULL);
pthread_join(producer_thread, NULL);
pthread_join(consumer_thread, NULL);
return 0;
}
线程局部存储实战
下面的示例利用线程局部存储(TLS)存储每个线程的特定状态(线程ID),展示其在复杂多线程环境中的优势。
#include <pthread.h>
#include <stdio.h>
pthread_key_t thread_id_key;
pthread_once_t thread_id_key_once = PTHREAD_ONCE_INIT;
void init_thread_id_key() {
pthread_key_create(&thread_id_key, NULL);
}
void *print_thread_id(void *arg) {
pthread_once(&thread_id_key_once, init_thread_id_key);
pthread_setspecific(thread_id_key, (void *)pthread_self());
while (1) {
printf("Thread ID (from TLS): %lu\n", (unsigned long)pthread_getspecific(thread_id_key));
sleep(1);
}
}
int main() {
pthread_t threads[3];
for (int i = 0; i < 3; ++i) {
pthread_create(&threads[i], NULL, print_thread_id, NULL);
}
for (int i = 0; i < 3; ++i) {
pthread_join(threads[i], NULL);
}
return 0;
}
以上四个示例分别展示了Pthreads在创建多线程、使用互斥量进行同步、应用条件变量协调线程协作,以及利用线程局部存储存储线程特定状态方面的实战应用。通过这些示例,可以更好地理解和掌握Pthreads在多线程编程中的实际应用。