linux线程基础
哪吒 2023/6/15
# Linux线程基础
# 1. 线程概念与特性
# 1.1 什么是线程
线程(Thread)是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 线程函数示例
void* thread_function(void* arg) {
int thread_id = *(int*)arg;
printf("线程 %d 正在运行\n", thread_id);
// 模拟工作
for(int i = 0; i < 5; i++) {
printf("线程 %d: 工作步骤 %d\n", thread_id, i + 1);
sleep(1);
}
printf("线程 %d 完成工作\n", thread_id);
return NULL;
}
# 1.2 线程与进程的区别
特性 | 进程 | 线程 |
---|---|---|
资源占用 | 独立的内存空间 | 共享进程内存空间 |
创建开销 | 较大 | 较小 |
通信方式 | IPC(管道、消息队列等) | 共享内存、信号量 |
切换开销 | 较大 | 较小 |
独立性 | 高度独立 | 相互依赖 |
# 1.3 线程的优势
- 并发执行:多个线程可以同时执行不同的任务
- 资源共享:线程间可以方便地共享数据
- 响应性:提高程序的响应速度
- 经济性:创建和切换开销小
# 2. POSIX线程(pthread)
# 2.1 pthread库简介
POSIX线程(pthread)是Linux系统中标准的线程API,提供了创建、管理和同步线程的函数。
// 编译时需要链接pthread库
// gcc -o program program.c -lpthread
# 2.2 线程创建与管理
# 创建线程
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
pthread_t thread1, thread2;
int thread1_id = 1, thread2_id = 2;
int result;
// 创建线程1
result = pthread_create(&thread1, NULL, thread_function, &thread1_id);
if (result != 0) {
printf("创建线程1失败\n");
exit(1);
}
// 创建线程2
result = pthread_create(&thread2, NULL, thread_function, &thread2_id);
if (result != 0) {
printf("创建线程2失败\n");
exit(1);
}
printf("主线程:已创建两个工作线程\n");
// 等待线程完成
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("主线程:所有线程已完成\n");
return 0;
}
# 线程属性设置
#include <pthread.h>
void create_detached_thread() {
pthread_t thread;
pthread_attr_t attr;
// 初始化线程属性
pthread_attr_init(&attr);
// 设置为分离状态
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 设置栈大小
size_t stack_size = 1024 * 1024; // 1MB
pthread_attr_setstacksize(&attr, stack_size);
// 创建线程
pthread_create(&thread, &attr, thread_function, NULL);
// 销毁属性对象
pthread_attr_destroy(&attr);
}
# 3. 线程同步机制
# 3.1 互斥锁(Mutex)
互斥锁用于保护共享资源,确保同一时间只有一个线程可以访问临界区。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 全局变量和互斥锁
int shared_counter = 0;
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
void* increment_counter(void* arg) {
int thread_id = *(int*)arg;
for(int i = 0; i < 100000; i++) {
// 加锁
pthread_mutex_lock(&counter_mutex);
// 临界区:修改共享变量
shared_counter++;
// 解锁
pthread_mutex_unlock(&counter_mutex);
}
printf("线程 %d 完成计数\n", thread_id);
return NULL;
}
int main() {
pthread_t threads[3];
int thread_ids[3] = {1, 2, 3};
// 创建多个线程
for(int i = 0; i < 3; i++) {
pthread_create(&threads[i], NULL, increment_counter, &thread_ids[i]);
}
// 等待所有线程完成
for(int i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
}
printf("最终计数值: %d\n", shared_counter);
// 销毁互斥锁
pthread_mutex_destroy(&counter_mutex);
return 0;
}
# 3.2 条件变量(Condition Variable)
条件变量用于线程间的条件同步,允许线程等待特定条件的发生。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
// 生产者-消费者模型
#define BUFFER_SIZE 10
int buffer[BUFFER_SIZE];
int buffer_count = 0;
int in = 0, out = 0;
pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t buffer_not_full = PTHREAD_COND_INITIALIZER;
pthread_cond_t buffer_not_empty = PTHREAD_COND_INITIALIZER;
// 生产者线程
void* producer(void* arg) {
int producer_id = *(int*)arg;
for(int i = 0; i < 20; i++) {
pthread_mutex_lock(&buffer_mutex);
// 等待缓冲区不满
while(buffer_count == BUFFER_SIZE) {
printf("生产者 %d: 缓冲区满,等待...\n", producer_id);
pthread_cond_wait(&buffer_not_full, &buffer_mutex);
}
// 生产数据
int item = i + producer_id * 100;
buffer[in] = item;
in = (in + 1) % BUFFER_SIZE;
buffer_count++;
printf("生产者 %d: 生产了 %d,缓冲区数量: %d\n",
producer_id, item, buffer_count);
// 通知消费者
pthread_cond_signal(&buffer_not_empty);
pthread_mutex_unlock(&buffer_mutex);
usleep(100000); // 0.1秒
}
return NULL;
}
// 消费者线程
void* consumer(void* arg) {
int consumer_id = *(int*)arg;
for(int i = 0; i < 20; i++) {
pthread_mutex_lock(&buffer_mutex);
// 等待缓冲区不空
while(buffer_count == 0) {
printf("消费者 %d: 缓冲区空,等待...\n", consumer_id);
pthread_cond_wait(&buffer_not_empty, &buffer_mutex);
}
// 消费数据
int item = buffer[out];
out = (out + 1) % BUFFER_SIZE;
buffer_count--;
printf("消费者 %d: 消费了 %d,缓冲区数量: %d\n",
consumer_id, item, buffer_count);
// 通知生产者
pthread_cond_signal(&buffer_not_full);
pthread_mutex_unlock(&buffer_mutex);
usleep(150000); // 0.15秒
}
return NULL;
}
# 3.3 读写锁(Read-Write Lock)
读写锁允许多个读者同时访问,但写者独占访问。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
// 共享数据和读写锁
int shared_data = 0;
pthread_rwlock_t data_rwlock = PTHREAD_RWLOCK_INITIALIZER;
// 读者线程
void* reader(void* arg) {
int reader_id = *(int*)arg;
for(int i = 0; i < 5; i++) {
// 获取读锁
pthread_rwlock_rdlock(&data_rwlock);
printf("读者 %d: 读取数据 = %d\n", reader_id, shared_data);
sleep(1);
// 释放读锁
pthread_rwlock_unlock(&data_rwlock);
sleep(1);
}
return NULL;
}
// 写者线程
void* writer(void* arg) {
int writer_id = *(int*)arg;
for(int i = 0; i < 3; i++) {
// 获取写锁
pthread_rwlock_wrlock(&data_rwlock);
shared_data += 10;
printf("写者 %d: 写入数据 = %d\n", writer_id, shared_data);
sleep(2);
// 释放写锁
pthread_rwlock_unlock(&data_rwlock);
sleep(2);
}
return NULL;
}
# 4. 线程池实现
# 4.1 简单线程池设计
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_THREADS 4
#define MAX_QUEUE 100
// 任务结构
typedef struct {
void (*function)(void* arg);
void* argument;
} task_t;
// 线程池结构
typedef struct {
pthread_t threads[MAX_THREADS];
task_t task_queue[MAX_QUEUE];
int queue_front;
int queue_rear;
int queue_size;
int shutdown;
pthread_mutex_t queue_mutex;
pthread_cond_t queue_not_empty;
pthread_cond_t queue_not_full;
} thread_pool_t;
thread_pool_t pool;
// 工作线程函数
void* worker_thread(void* arg) {
while(1) {
pthread_mutex_lock(&pool.queue_mutex);
// 等待任务
while(pool.queue_size == 0 && !pool.shutdown) {
pthread_cond_wait(&pool.queue_not_empty, &pool.queue_mutex);
}
// 检查是否需要关闭
if(pool.shutdown) {
pthread_mutex_unlock(&pool.queue_mutex);
break;
}
// 取出任务
task_t task = pool.task_queue[pool.queue_front];
pool.queue_front = (pool.queue_front + 1) % MAX_QUEUE;
pool.queue_size--;
// 通知可以添加新任务
pthread_cond_signal(&pool.queue_not_full);
pthread_mutex_unlock(&pool.queue_mutex);
// 执行任务
task.function(task.argument);
}
return NULL;
}
// 初始化线程池
void thread_pool_init() {
pool.queue_front = 0;
pool.queue_rear = 0;
pool.queue_size = 0;
pool.shutdown = 0;
pthread_mutex_init(&pool.queue_mutex, NULL);
pthread_cond_init(&pool.queue_not_empty, NULL);
pthread_cond_init(&pool.queue_not_full, NULL);
// 创建工作线程
for(int i = 0; i < MAX_THREADS; i++) {
pthread_create(&pool.threads[i], NULL, worker_thread, NULL);
}
printf("线程池初始化完成,创建了 %d 个工作线程\n", MAX_THREADS);
}
// 添加任务到线程池
void thread_pool_add_task(void (*function)(void*), void* argument) {
pthread_mutex_lock(&pool.queue_mutex);
// 等待队列不满
while(pool.queue_size == MAX_QUEUE) {
pthread_cond_wait(&pool.queue_not_full, &pool.queue_mutex);
}
// 添加任务
pool.task_queue[pool.queue_rear].function = function;
pool.task_queue[pool.queue_rear].argument = argument;
pool.queue_rear = (pool.queue_rear + 1) % MAX_QUEUE;
pool.queue_size++;
// 通知工作线程
pthread_cond_signal(&pool.queue_not_empty);
pthread_mutex_unlock(&pool.queue_mutex);
}
// 示例任务函数
void example_task(void* arg) {
int task_id = *(int*)arg;
printf("执行任务 %d,线程ID: %lu\n", task_id, pthread_self());
sleep(2); // 模拟工作
printf("任务 %d 完成\n", task_id);
}
# 5. 线程安全与最佳实践
# 5.1 线程安全的概念
线程安全是指在多线程环境下,程序能够正确地处理多个线程同时访问共享资源的情况。
# 5.2 常见的线程安全问题
# 竞态条件(Race Condition)
// 不安全的代码示例
int global_counter = 0;
void* unsafe_increment(void* arg) {
for(int i = 0; i < 100000; i++) {
// 这里存在竞态条件
global_counter++; // 非原子操作
}
return NULL;
}
// 安全的代码示例
pthread_mutex_t safe_mutex = PTHREAD_MUTEX_INITIALIZER;
int safe_counter = 0;
void* safe_increment(void* arg) {
for(int i = 0; i < 100000; i++) {
pthread_mutex_lock(&safe_mutex);
safe_counter++; // 受保护的操作
pthread_mutex_unlock(&safe_mutex);
}
return NULL;
}
# 死锁(Deadlock)
// 可能导致死锁的代码
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
void* thread1_func(void* arg) {
pthread_mutex_lock(&mutex1);
printf("线程1获得mutex1\n");
sleep(1);
pthread_mutex_lock(&mutex2); // 可能死锁
printf("线程1获得mutex2\n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return NULL;
}
void* thread2_func(void* arg) {
pthread_mutex_lock(&mutex2);
printf("线程2获得mutex2\n");
sleep(1);
pthread_mutex_lock(&mutex1); // 可能死锁
printf("线程2获得mutex1\n");
pthread_mutex_unlock(&mutex1);
pthread_mutex_unlock(&mutex2);
return NULL;
}
// 避免死锁的方法:统一加锁顺序
void* safe_thread1_func(void* arg) {
pthread_mutex_lock(&mutex1); // 总是先锁mutex1
pthread_mutex_lock(&mutex2); // 再锁mutex2
// 执行工作
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return NULL;
}
# 5.3 最佳实践
- 最小化锁的范围:只在必要时加锁,尽快释放
- 避免嵌套锁:减少死锁的可能性
- 使用RAII模式:确保资源正确释放
- 优先使用高级同步原语:如条件变量、读写锁
- 线程局部存储:减少共享数据
// 线程局部存储示例
__thread int thread_local_var = 0;
void* thread_function(void* arg) {
int thread_id = *(int*)arg;
// 每个线程都有自己的副本
thread_local_var = thread_id * 100;
printf("线程 %d 的局部变量: %d\n", thread_id, thread_local_var);
return NULL;
}
# 6. 性能优化与调试
# 6.1 性能监控
#include <sys/time.h>
// 性能计时器
double get_time() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
void* performance_test_thread(void* arg) {
double start_time = get_time();
// 执行工作
for(int i = 0; i < 1000000; i++) {
// 模拟计算
}
double end_time = get_time();
printf("线程执行时间: %.6f 秒\n", end_time - start_time);
return NULL;
}
# 6.2 调试技巧
# 使用gdb调试多线程程序
gdb ./program
(gdb) set scheduler-locking on # 锁定调度器
(gdb) info threads # 查看所有线程
(gdb) thread 2 # 切换到线程2
(gdb) bt # 查看调用栈
# 使用valgrind检测线程问题
valgrind --tool=helgrind ./program
valgrind --tool=drd ./program
# 7. 总结
Linux线程编程是系统编程的重要组成部分,掌握以下要点:
- 基础概念:理解线程与进程的区别
- POSIX线程:熟练使用pthread API
- 同步机制:正确使用互斥锁、条件变量、读写锁
- 线程安全:避免竞态条件和死锁
- 性能优化:合理设计线程架构
- 调试技巧:使用专业工具定位问题
通过实践这些概念和技术,可以编写出高效、安全的多线程程序。