news 2026/5/14 10:48:39

复习2——线程(pthread)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
复习2——线程(pthread)

线程(pthread)知识点整理

1. 线程概念与特点

线程 vs 进程

特征进程线程
资源分配最小资源分配单位最小执行单位
资源共享私有资源空间共享进程资源,部分私有
通信方式IPC(复杂)直接通信(简单)
创建开销小(轻量级进程)
稳定性相对较低
效率相对较低高(节省30%资源)

线程优点

  1. 资源共享:共享进程内的全局变量、堆区、文件描述符等

  2. 高效:创建销毁开销小,上下文切换快

  3. 并发性好:适合I/O密集型任务

  4. 简化通信:直接共享内存,无需复杂IPC

线程缺点

  1. 稳定性差:一个线程崩溃可能影响整个进程

  2. 调试复杂:gdb调试需要特殊命令

  3. 缺乏保护:线程间没有内存保护

2. POSIX线程编程框架

三部曲:创建线程 → 线程执行 → 资源回收

编译与头文件

#include <pthread.h> // 线程头文件 gcc -g -pthread source.c // 编译命令(-lpthread)

永久设置编译别名

# 编辑 ~/.bashrc alias gcc='gcc -g -pthread' # 生效配置 source ~/.bashrc

3. 线程创建与管理

3.1 创建线程

int pthread_create( pthread_t *thread, // 线程ID(出参) const pthread_attr_t *attr, // 线程属性(NULL为默认) void *(*start_routine)(void *), // 线程回调函数 void *arg // 回调函数参数 );

重要特性

  • 一次只能创建一个线程

  • 主线程退出,所有子线程也退出

  • 线程ID是CPU维护的唯一标识

  • 多个线程可执行同一回调函数

3.2 获取线程ID

pthread_t pthread_self(void); // 获取当前线程ID

3.3 查看线程信息命令

ps -eLf # 查看所有线程 ps -eLo pid,ppid,lwp,stat,comm # 查看LWP(轻量级进程) pstree # 查看线程树结构

4. 线程退出与回收

4.1 线程退出方式

// 1. 自行退出(自杀) void pthread_exit(void *retval); // retval: 退出状态 // 2. 强制退出(他杀) int pthread_cancel(pthread_t thread);

4.2 线程资源回收

int pthread_join(pthread_t thread, void **retval);

回收策略

  1. 有限时间结束 →pthread_join阻塞等待

  2. 可能休眠阻塞 → 超时后强制回收

  3. 长期运行 → 不回收(可能导致资源泄漏)

4.3 分离属性

// 方法1:设置属性 pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&tid, &attr, func, NULL); pthread_attr_destroy(&attr); // 方法2:直接分离 int pthread_detach(pthread_t thread);

5. 线程参数传递

5.1 传递整型参数

// 主线程 int value = 100; pthread_create(&tid, NULL, func, (void *)(long)value); // 子线程 void *func(void *arg) { int val = (int)(long)arg; // 使用val return NULL; }

5.2 传递字符串参数

// 栈区字符数组(危险:线程结束后可能失效) char str[] = "hello"; pthread_create(&tid, NULL, func, str); // 堆区字符串(安全) char *str = malloc(128); strcpy(str, "hello"); pthread_create(&tid, NULL, func, str); // 线程结束后需要free

5.3 传递结构体参数

typedef struct { int id; char name[32]; float score; } Student; // 主线程 Student stu = {1, "Tom", 90.5}; pthread_create(&tid, NULL, func, &stu); // 子线程 void *func(void *arg) { Student *stu = (Student *)arg; printf("ID:%d, Name:%s, Score:%.1f\n", stu->id, stu->name, stu->score); return NULL; }

5.4 计算器示例

typedef struct { float a; float b; char op; // + - * / float result; } Calculator; void *calc_func(void *arg) { Calculator *calc = (Calculator *)arg; switch(calc->op) { case '+': calc->result = calc->a + calc->b; break; case '-': calc->result = calc->a - calc->b; break; case '*': calc->result = calc->a * calc->b; break; case '/': if(calc->b != 0) calc->result = calc->a / calc->b; else calc->result = 0; break; } return NULL; }

6. 线程返回值

返回值传递原理

  • pthread_exit(void *retval)→ 返回地址

  • pthread_join(tid, void **retval)→ 接收地址

有效返回地址类型:

  1. 全局变量(可直接访问,意义不大)

  2. 静态变量(生命周期长)

  3. 堆区变量(推荐:可控制生命周期)

// 示例:返回堆区字符串 void *thread_func(void *arg) { char *result = malloc(128); strcpy(result, "Thread completed successfully"); pthread_exit(result); } int main() { pthread_t tid; char *retval; pthread_create(&tid, NULL, thread_func, NULL); pthread_join(tid, (void **)&retval); printf("Thread returned: %s\n", retval); free(retval); // 必须释放 return 0; }

7. 线程清理函数

void cleanup_func(void *arg) { printf("Cleanup: %s\n", (char *)arg); } void *thread_func(void *arg) { // 注册清理函数 pthread_cleanup_push(cleanup_func, "Resource cleanup"); // 线程工作代码 // do something... // 执行清理函数(参数为0则不执行) pthread_cleanup_pop(1); // 1:执行, 0:不执行 return NULL; }

8. 线程互斥(Mutex)

互斥锁框架

// 1. 定义互斥锁 pthread_mutex_t mutex; // 2. 初始化 pthread_mutex_init(&mutex, NULL); // 3. 加锁 pthread_mutex_lock(&mutex); // 临界区代码 pthread_mutex_unlock(&mutex); // 4. 解锁 // 5. 销毁 pthread_mutex_destroy(&mutex);

非阻塞加锁

int pthread_mutex_trylock(pthread_mutex_t *mutex); // 成功: 0, 失败: EBUSY(锁已被占用)

互斥锁示例:共享缓冲区

#include <stdio.h> #include <pthread.h> #include <string.h> #include <unistd.h> char buffer[1024]; pthread_mutex_t mutex; void *writer_thread(void *arg) { char *msg = (char *)arg; for(int i = 0; i < 5; i++) { pthread_mutex_lock(&mutex); strcpy(buffer, msg); printf("%s wrote: %s\n", msg, buffer); pthread_mutex_unlock(&mutex); usleep(100000); // 100ms } return NULL; } int main() { pthread_t tid1, tid2; pthread_mutex_init(&mutex, NULL); pthread_create(&tid1, NULL, writer_thread, "Thread1"); pthread_create(&tid2, NULL, writer_thread, "Thread2"); pthread_join(tid1, NULL); pthread_join(tid2, NULL); pthread_mutex_destroy(&mutex); return 0; }

9. 线程同步(信号量)

信号量框架

#include <semaphore.h> // 1. 定义信号量 sem_t sem; // 2. 初始化(二值信号量) sem_init(&sem, 0, 1); // 参数:信号量,pshared(0=线程), 初始值 // 3. P操作(申请资源) sem_wait(&sem); // 临界区 sem_post(&sem); // 4. V操作(释放资源) // 5. 销毁 sem_destroy(&sem);

同步示例:生产者-消费者

#include <stdio.h> #include <pthread.h> #include <semaphore.h> #include <string.h> char buffer[256]; sem_t sem_empty; // 缓冲区空信号量 sem_t sem_full; // 缓冲区满信号量 void *input_thread(void *arg) { while(1) { sem_wait(&sem_empty); // 等待缓冲区空 printf("Enter message: "); fgets(buffer, sizeof(buffer), stdin); buffer[strlen(buffer)-1] = '\0'; // 去掉换行符 if(strcmp(buffer, "quit") == 0) { sem_post(&sem_full); break; } sem_post(&sem_full); // 通知处理线程 } return NULL; } void *process_thread(void *arg) { while(1) { sem_wait(&sem_full); // 等待数据 if(strcmp(buffer, "quit") == 0) { sem_post(&sem_empty); break; } printf("Processed: Length=%lu\n", strlen(buffer)); sem_post(&sem_empty); // 通知可继续输入 } return NULL; } int main() { pthread_t tid1, tid2; sem_init(&sem_empty, 0, 1); // 初始缓冲区为空 sem_init(&sem_full, 0, 0); // 初始无数据 pthread_create(&tid1, NULL, input_thread, NULL); pthread_create(&tid2, NULL, process_thread, NULL); pthread_join(tid1, NULL); pthread_join(tid2, NULL); sem_destroy(&sem_empty); sem_destroy(&sem_full); return 0; }

10. 综合示例:火车票售票系统

#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> #define TICKET_COUNT 100 int tickets = TICKET_COUNT; pthread_mutex_t mutex; int window1_count = 0; int window2_count = 0; void *sell_tickets(void *arg) { char *window_name = (char *)arg; while(1) { pthread_mutex_lock(&mutex); if(tickets > 0) { printf("%s 卖出车票 %d\n", window_name, TICKET_COUNT - tickets + 1); tickets--; if(strcmp(window_name, "窗口1") == 0) window1_count++; else window2_count++; pthread_mutex_unlock(&mutex); usleep(100000); // 模拟卖票时间 } else { pthread_mutex_unlock(&mutex); break; } } return NULL; } int main() { pthread_t tid1, tid2; pthread_mutex_init(&mutex, NULL); pthread_create(&tid1, NULL, sell_tickets, "窗口1"); pthread_create(&tid2, NULL, sell_tickets, "窗口2"); pthread_join(tid1, NULL); pthread_join(tid2, NULL); printf("\n售票统计:\n"); printf("窗口1卖出: %d张\n", window1_count); printf("窗口2卖出: %d张\n", window2_count); printf("总共卖出: %d张\n", TICKET_COUNT - tickets); pthread_mutex_destroy(&mutex); return 0; }

11. 线程控制函数对比

进程操作线程操作功能
fork()pthread_create()创建
getpid()pthread_self()获取ID
exit()pthread_exit()退出
wait()pthread_join()等待回收
kill()pthread_cancel()强制终止
atexit()pthread_cleanup_push/pop()清理函数

12. 死锁与预防

死锁产生条件

  1. 互斥条件:资源只能被一个线程使用

  2. 请求与保持:线程持有资源并请求新资源

  3. 不可剥夺:资源只能由持有者释放

  4. 循环等待:线程间形成等待环

预防策略

  1. 按顺序申请资源:所有线程按相同顺序申请锁

  2. 使用超时机制pthread_mutex_trylock+ 超时重试

  3. 避免嵌套锁:尽量减少锁的嵌套层次

  4. 使用资源层级:为资源分配优先级

避免死锁示例

// 按固定顺序获取锁 void safe_operation(pthread_mutex_t *lock1, pthread_mutex_t *lock2) { // 总是先获取lock1,再获取lock2 pthread_mutex_lock(lock1); pthread_mutex_lock(lock2); // 临界区操作 pthread_mutex_unlock(lock2); pthread_mutex_unlock(lock1); }

13. 线程调度与让出CPU

pthread_yield(); // 主动让出CPU(建议使用) usleep(1000); // 休眠方式让出CPU

14. 综合练习:餐厅管理系统

#include <stdio.h> #include <pthread.h> #include <semaphore.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define TABLE_COUNT 3 typedef struct { char name[100]; int total_num; int call_num; pthread_mutex_t lock; } Restaurant; Restaurant rest; sem_t tables; // 餐桌信号量 void *customer(void *arg) { int id = *(int *)arg; printf("顾客%d [%s] 正在用餐...\n", id, rest.name); sleep(rand() % 3 + 1); // 用餐时间 printf("顾客%d [%s] 离开\n", id, rest.name); sem_post(&tables); // 释放餐桌 free(arg); return NULL; } void *waiter(void *arg) { while(1) { pthread_mutex_lock(&rest.lock); if(rest.call_num < rest.total_num) { sem_wait(&tables); // 等待空桌 int *id = malloc(sizeof(int)); *id = ++rest.call_num; pthread_t tid; pthread_create(&tid, NULL, customer, id); pthread_detach(tid); printf("服务员叫号: %d [%s]\n", rest.call_num, rest.name); } else { pthread_mutex_unlock(&rest.lock); break; } pthread_mutex_unlock(&rest.lock); sleep(1); } return NULL; } int main() { pthread_t waiter_tid; // 初始化 sem_init(&tables, 0, TABLE_COUNT); pthread_mutex_init(&rest.lock, NULL); rest.total_num = 0; rest.call_num = 0; // 输入顾客信息 printf("请输入顾客姓名: "); fgets(rest.name, sizeof(rest.name), stdin); rest.name[strlen(rest.name)-1] = '\0'; printf("请输入顾客总数: "); scanf("%d", &rest.total_num); getchar(); // 清除缓冲区 // 启动服务员线程 pthread_create(&waiter_tid, NULL, waiter, NULL); pthread_join(waiter_tid, NULL); // 清理 sem_destroy(&tables); pthread_mutex_destroy(&rest.lock); printf("所有顾客已服务完毕!\n"); return 0; }

15. 线程调试技巧

GDB调试线程

gdb ./a.out (gdb) run (gdb) info threads # 查看所有线程 (gdb) thread 2 # 切换到线程2 (gdb) bt # 查看线程调用栈 (gdb) thread apply all bt # 查看所有线程调用栈

调试建议

  1. 编译带调试信息gcc -g -pthread

  2. 使用线程局部变量:避免全局变量冲突

  3. 添加调试输出:打印线程ID和状态

  4. 逐步测试:先测试单线程,再测试多线程

16.总结

多线程编程的关键在于正确处理共享资源同步互斥问题。合理使用互斥锁和信号量可以避免竞态条件和死锁,同时需要注意线程的生命周期管理和资源回收。在实际开发中,应根据具体需求选择合适的线程模型和同步机制。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 21:47:46

社交网络数据质量治理:经验与教训

社交网络数据质量治理&#xff1a;从踩坑到进阶的实战经验 一、引言&#xff1a;社交网络的“数据烂尾楼”困境 钩子&#xff1a;你遇到过这些“反人类”社交体验吗&#xff1f; 刷到完全不感兴趣的推荐&#xff1f;比如你是健身达人&#xff0c;却总收到美妆广告&#xff1…

作者头像 李华
网站建设 2026/5/13 7:26:19

std::greater结构体用在sort和lower_bound

https://cn.bing.com/search?pglt417&qgreater%3Cstring%3E std::sort(numbers, numbers 5, std::greater<int>());&#xff0c;std::greater{}也可以 #if _LIBCPP_STD_VER > 14 template <class _Tp void> #else template <class _Tp> #endif s…

作者头像 李华
网站建设 2026/5/13 15:17:53

当数字员工搭载AI销冠系统,如何迅速提升销售效率?

数字员工通过引入AI销冠系统&#xff0c;能够显著优化业务流程&#xff0c;降低企业运营成本&#xff0c;并提升整体效率。数字员工的智能化特性使其能够自动化处理大量客户交互&#xff0c;如电话回访和信息收集&#xff0c;减少了对传统人工客服的依赖。这不仅提高了工作效率…

作者头像 李华
网站建设 2026/5/9 21:55:32

还在手动处理退款?RPA自动处理希音退款,效率提升20倍!⚡

还在手动处理退款&#xff1f;RPA自动处理希音退款&#xff0c;效率提升20倍&#xff01;⚡ "深夜11点&#xff0c;客服团队还在电脑前逐条审核退款申请&#xff0c;同样的操作重复了上百遍...这样的加班场景该终结了&#xff01;" 一、痛点直击&#xff1a;退款处理…

作者头像 李华
网站建设 2026/5/12 16:36:21

Open-AutoGLM快递路径预测黑科技(基于时空图神经网络的大模型应用)

第一章&#xff1a;Open-AutoGLM 快递轨迹追踪Open-AutoGLM 是一个基于大语言模型与自动化推理框架的智能物流解决方案&#xff0c;专注于快递轨迹的实时解析与状态预测。该系统能够从非结构化的物流日志中提取关键节点信息&#xff0c;并结合时间序列分析实现高精度的路径还原…

作者头像 李华