马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
目次
从第一性原理出发:为什么须要线程?
✅ 本质界说:
📌 使用根本语法:
线程之间的“并发”与“并行”的区别
线程安全与数据竞争(Race Condition)
如何让线程“安全地”访问数据?
完备示例:使用线程加速加法
从第一性原理出发:为什么须要线程?
想象一下:
你有一个步调,它要做许多事,好比:
- 下载文件
- 处理图片
- 打印日记
- 相应用户输入假如这些使命按次序来(单线程),那用户体验就会很差:好比下载还没完,界面就卡住了。
第一性标题来了:
能不能让步调同时做多件事,而不是排队一个个来?
能!用线程!
✅ 本质界说:
线程(Thread)是步调内部能并发实行的最小单元。
线程 = 步调里可以独立并发实行的一段使命。
一个步调至少有一个线程(主线程),你可以再创建多个线程来同时实行差别的代码块。
📌 使用根本语法:
std::thread 类就是 C++ 提供的“创建和管理线程”的工具。
- #include <iostream>#include <thread>
- void work() { std::cout << "工作线程正在运行\n";}int main() { std::thread t(work); // 创建一个线程实行 work() t.join(); // 等候这个线程实行完 std::cout << "主线程竣事\n";}
复制代码 表明:
- std::thread t(work);:创建了一个“工人线程”,去实行函数 work
- t.join();:告诉主线程“等工人做完再走”
- 末了主线程输出“主线程竣事”
创建线程的三种方式:
方法示例传函数名(函数指针)std::thread t(func);
传 lambda 表达式std::thread t([](){ ... });传函数对象(仿函数)std::thread t(Functor()); 线程的控制使用(join 和 detach)
你创建了一个新线程,但它和主线程是“并行运行的”。
接下来你必须做一件事(二选一):
使用阐明t.join()等候这个线程运行竣事,和主线程“合并”t.detach()把线程“放飞”,让它本身跑,主线程不再管它(配景线程) C++ 尺度要求:每个 std::thread 对象 在烧毁前必须被 join 或 detach!
留意:主线程大概在子线程还没竣事时就退出了,这种线程叫做 配景线程。
线程之间的“并发”与“并行”的区别
概念寄义并发(concurrent)逻辑上“同时”运行,现实上轮替交替实行(单核CPU)并行(parallel)真正的同时实行(多核CPU,每个线程跑在差别核上) 线程安全与数据竞争(Race Condition)
多个线程访问同一块数据会出标题!
- int counter = 0;
- void add() {
- for (int i = 0; i < 100000; ++i)
- ++counter;
- }
- int main() {
- std::thread t1(add);
- std::thread t2(add);
- t1.join();
- t2.join();
- std::cout << counter << std::endl; // ❓ 输出不是 200000?!
- }
复制代码 为什么?
- 由于两个线程在同时修改同一个变量
- 出现了所谓的 数据竞争(Race Condition)
如何让线程“安全地”访问数据?
使用互斥锁(std::mutex)
- #include <mutex>
- std::mutex mtx;
- void add() {
- for (int i = 0; i < 100000; ++i) {
- std::lock_guard<std::mutex> lock(mtx); // 自动加锁/解锁
- ++counter;
- }
- }
复制代码 使用 lock_guard 是今世 C++ 推荐的做法,RAII 主动开释锁。
完备示例:使用线程加速加法
- #include <iostream>#include <thread>
- void printRange(int start, int end) { for (int i = start; i <= end; ++i) std::cout << i << " ";}int main() { std::thread t1(printRange, 1, 50); std::thread t2(printRange, 51, 100); t1.join(); t2.join(); std::cout << "\nDone\n";}
复制代码 输出大概是:
- 1 2 3 51 52 53 ... 50 100
复制代码 次序是“乱的”!由于是并发实行的结果。
|