news 2026/2/11 15:58:44

复习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/2/8 11:22:09

【拯救HMI】构建您的HMI组件库:提升设计一致性与开发效率的终极法则

如何确保不同设计师、不同项目之间的HMI保持统一风格&#xff1f;答案在于构建和维护一个企业级的、可复用的HMI组件库。什么是HMI组件库&#xff1f;它是一套预制的、标准化的UI元素集合&#xff0c;包括按钮、输入框、选择器、警报框、趋势图控件等&#xff0c;每个组件都包含…

作者头像 李华
网站建设 2026/2/3 23:36:55

大厂都这么干,没人管么?

昨天写闲鱼的文章引起了很多读者的讨论&#xff0c;看来不止我一个人觉得烦&#xff0c;很多人也都有同感。 其实我的观点很直接&#xff0c;不打扰&#xff0c;也是产品体验的一部分。 我在评论区区还看到这么一条留言&#xff0c;觉得有必要单独拿出来聊一聊&#xff0c;因为…

作者头像 李华
网站建设 2026/2/8 0:22:21

[Windows] 360极速浏览器v23.1.1137.64绿简版

[Windows] 360极速浏览器v23.1.1137.64绿简版 链接&#xff1a;https://pan.xunlei.com/s/VOgv2MyZyLbh8szAIb79dT56A1?pwd28cb# 鉴于还有好多人或公司仍然在使用360的浏览器&#xff0c;特制作此绿色版&#xff0c;方便携带使用。 感谢 360团队 360极速浏览器&#xff0c…

作者头像 李华