一、并发编程概述:并发与并行的区别、多线程的应用场景、C++11线程库简介
各位同学,咱们今天聊聊并发编程的入门话题。说实话,我刚开始接触这个领域时,也被一堆概念绕得头晕。但别担心,我会用最直白的方式讲清楚。
1.1 并发 vs 并行:别再傻傻分不清
这两个词,我见过太多人混着用。包括我自己,刚入行时也闹过笑话。
并发(Concurrency),说白了就是「看起来同时做多件事」。比如你一边写代码,一边听音乐。实际上你的大脑在快速切换——写几行代码,切到音乐,再切回来。这就是并发。
并行(Parallelism),才是「真正的同时做多件事」。比如你左手敲键盘,右手拿鼠标。两个动作同时发生,互不干扰。
我打个比方:
- 并发 = 一个人吃三个馒头,一口一口轮着吃
- 并行 = 三个人同时吃三个馒头
嗯,这里要注意:并发是逻辑上的同时,并行是物理上的同时。单核CPU只能并发,多核CPU才能并行。
核心区别一句话:
并发是「处理多个任务的能力」,并行是「同时执行多个任务的能力」。
我在项目中遇到过这样一个场景:一个网络服务器要处理上千个客户端连接。如果用并行思路,开上千个线程?系统直接崩了。但用并发思路,用事件循环+线程池,轻松搞定。这就是并发设计的魅力。
1.2 多线程的应用场景:什么时候该用?
你可能会问:我写个简单的计算器,需要多线程吗?当然不用。但有些场景,不用多线程就是跟自己过不去。
典型场景一:UI响应 + 后台计算
比如你写个图像处理软件。用户点了个「滤镜」按钮,如果直接在UI线程里算,界面就卡死了。用户会以为程序崩溃了。正确的做法:开个工作线程去算,UI线程继续响应用户操作。
典型场景二:IO密集型任务
读文件、网络请求、数据库查询……这些操作大部分时间在等待。单线程做,CPU闲着,IO也慢。多线程可以让一个线程等IO时,另一个线程继续干活。
典型场景三:计算密集型任务
视频编码、科学计算、3D渲染……这些活儿CPU越猛越好。多核CPU上,开多个线程并行算,速度翻倍。
我曾经接手过一个日志分析系统,单线程处理一天的数据要跑8小时。改成多线程后,2小时搞定。老板当场给我加了鸡腿。
我的建议:
别为了用多线程而用多线程。先问自己三个问题:
- 这个任务能拆成独立的小任务吗?
- 拆开后,有没有共享数据需要保护?
- 多线程带来的性能提升,值得增加复杂度吗?
如果答案都是「是」,那就大胆上。
1.3 C++11线程库简介:终于等到你
在C++11之前,写多线程简直是噩梦。Windows用CreateThread,Linux用pthread,跨平台?想都别想。
C++11终于把线程支持纳入了标准库。说实话,我等这一天等了好多年。
核心组件一览:
| 组件 | 头文件 | 作用 |
|---|---|---|
| std::thread | <thread> | 创建和管理线程 |
| std::mutex | <mutex> | 互斥锁,保护共享数据 |
| std::lock_guard | <mutex> | RAII风格的锁管理 |
| std::atomic | <atomic> | 原子操作,无锁编程基础 |
| std::future | <future> | 异步任务,获取返回值 |
| std::condition_variable | <condition_variable> | 条件变量,线程间通知 |
来看个最简单的例子:
#include <iostream>
#include <thread>
void hello() {
std::cout << "Hello from thread!\n";
}
int main() {
std::thread t(hello); // 创建线程,执行hello函数
t.join(); // 等待线程结束
return 0;
}
就这么几行,一个跨平台的多线程程序就写好了。放在以前,你得写一堆平台相关的代码。
避坑指南:
我曾经犯过一个低级错误:忘记调用join()或detach(),导致程序崩溃。记住:每个std::thread对象在销毁前,必须明确是等待它(join)还是分离它(detach)。否则会触发std::terminate。
C++11线程库的设计哲学是「轻量、安全、可移植」。它没有提供线程池、读写锁这些高级工具,但提供了足够的基础构件。你可以用这些构件搭出任何你想要的东西。
我个人习惯是:能用标准库就不用第三方库。标准库的线程组件经过了全世界无数项目的检验,稳定性和可移植性都有保障。
本章知识体系
下面这张图,帮你理清本章的核心脉络:
这张图把本章的三个核心主题串起来了。从左到右,从理论到实践。你想想看,搞懂并发和并行的区别,知道什么时候该用多线程,再掌握C++11提供的工具,是不是心里就有底了?
好了,这一章就聊到这儿。记住:并发编程不是炫技,而是解决实际问题的利器。下一章我们深入std::thread的细节,看看线程到底怎么创建、怎么管理、怎么安全地结束。