1084 字
5 分钟
多线程入门:用C语言轻松实现并发任务
一、故事开场:一个人 vs 三个人
想象你在家里:
- 一个人版本:先看电视,看完一集再去玩游戏,玩累了再听音乐——三件事串行,总耗时 = 看 + 玩 + 听。
- 三个人版本:你、朋友 A、朋友 B 同时开工——并行推进,总耗时 ≈ 最长的一项。
计算机里的“线程”就是这位“朋友”。一个进程(你家)可以招很多线程(朋友)一起干活,共享客厅(内存),但各自带自己的记事本(栈)。
二、线程是什么?一句话记住
线程 = 轻量级的“子任务”,共享地址空间,切换成本比进程低得多。
三、Linux 下的“招工”神器:pthread
POSIX 线程(pthread)是 Linux/Unix 系统自带的线程库,用法简单,三步走:
- 写线程函数 → 2. 创建线程 → 3. 回收线程
四、最小可运行示例:让三条任务一起跑
下面这段代码同时启动三条“死循环”任务,每秒打印一次状态。先睹为快,再逐行拆解。
// 编译:gcc mini_thread.c -o mini_thread -pthread#include <pthread.h>#include <stdio.h>#include <unistd.h>
void* watch_tv(void* arg) { while (1) { printf("[看电视] 剧情太精彩啦!\n"); sleep(1); } return NULL;}
void* play_game(void* arg) { while (1) { printf(" [玩游戏] 五连绝世!\n"); sleep(1); } return NULL;}
void* listen_music(void* arg) { while (1) { printf(" [听音乐] 动次打次!\n"); sleep(1); } return NULL;}
int main() { pthread_t t1, t2, t3;
pthread_create(&t1, NULL, watch_tv, NULL); pthread_create(&t2, NULL, play_game, NULL); pthread_create(&t3, NULL, listen_music, NULL);
while (1) { // 主线程也陪跑 printf("[主线程] 我还活着\n"); sleep(1); } return 0;}运行效果(节选,实际交错):
[看电视] 剧情太精彩啦![主线程] 我还活着 [玩游戏] 五连绝世! [听音乐] 动次打次!...五、逐行拆解,10 分钟看懂
| 代码片段 | 作用 |
|---|---|
pthread_t t1; | 定义线程“工牌”变量 |
pthread_create(&t1, NULL, 函数, 参数) | 招工:把函数变成线程 |
void* 函数(void* arg) | 线程入口,必须这个签名 |
sleep(1); | 让出 CPU,避免刷屏 |
while(1) | 演示用死循环,实际项目会放退出条件 |
六、改进版:让线程“下班”
死循环演示可以,生产环境必须可控退出 + 资源回收。思路:
- 全局放一面“停工序牌”:
volatile int g_stop = 0; - 线程循环检测:
while (!g_stop) { ... } - 主线程下班后挂牌:
g_stop = 1; - 等待所有人收工:
pthread_join
完整代码:
// gcc better_thread.c -o better_thread -pthread#include <pthread.h>#include <stdio.h>#include <unistd.h>
volatile int g_stop = 0; // ① 停工标志
typedef struct { // ② 打包参数 const char* name; int gap;} Task;
void* worker(void* arg) { Task* t = (Task*)arg; while (!g_stop) { printf("[%s] 工作中...\n", t->name); sleep(t->gap); } printf("[%s] 收到停工通知,拜拜!\n", t->name); return NULL;}
int main() { pthread_t t[3]; Task tasks[3] = { {"看电视", 1}, {"玩游戏", 2}, {"听音乐", 3} };
for (int i = 0; i < 3; ++i) pthread_create(&t[i], NULL, worker, &tasks[i]);
sleep(8); // 让任务跑 8 秒 g_stop = 1; // ③ 挂牌停工
for (int i = 0; i < 3; ++i) pthread_join(t[i], NULL); // ④ 收工清点
puts("主线程:所有人都下班了,关灯!"); return 0;}运行 8 秒后,你会看到三条线程有序退出,资源干净回收。
七、常见坑位排雷
| 坑 | 现象 | 避坑指南 |
|---|---|---|
忘记 -pthread | 编译报错“未定义引用” | 永远带 -pthread:既链接库又开宏 |
主线程直接 return | 子线程被强制“团灭” | 用 pthread_join 或 pthread_detach 收尾 |
printf 交错 | 输出乱序 | 正常现象,加锁或整行缓冲可缓解 |
| 传参用局部变量 | 线程拿到野指针 | 传堆内存或全局/静态变量 |
| 死循环无退出 | 程序关不掉 | 设退出标志或外部信号 |
八、下一步学什么
- 同步原语:互斥锁
pthread_mutex_t、条件变量pthread_cond_t - 经典模型:生产者-消费者、读者-写者
- 线程池:统一管理任务,避免频繁创建销毁
- C11
<threads.h>与 C++std::thread:跨平台新接口
九、一分钟总结
- 线程 = 共享内存的多任务,切换快。
- pthread 三板斧:
create创建 →join/detach回收 → 同步原语保安全。 - 写线程函数注意签名
void* (*)(void*),传参注意生命周期。 - 永远加
-pthread,永远考虑退出策略。
把示例粘进终端,亲手编译运行一次,你就已经踏进“并发”的大门啦!祝你玩得开心,bug 少少。
最后更新于 2024-02-20,距今已过 640 天
部分内容可能已过时