C++线程、并发、并行
【欢迎关注编码小哥,学习更多实用的编程方法和技巧】
一、线程、并发和并行的概念
1、线程定义:
- 程序执行的最小单元
- 轻量级的进程
- 共享同一进程的内存空间
特征:
- 独立的执行路径
- 共享进程资源
- 上下文切换开销较小
类型:
用户线程
内核线程
协程
2、并发定义:
- 多个任务在同一时间段内交替执行
- 宏观同时进行,微观交替
特点:
- 单核CPU也可实现
- 通过时间片轮转
- 提高系统响应性
- 资源利用率提升
实现方式:
进程间并发
线程间并发
协程并发
3、并行定义:
- 多个任务同时执行
- 物理上同时运行
特点:
- 多核/多处理器
- 真正同时执行
- 性能显著提升
实现方式:
- 多核处理
- 分布式计算
- GPU并行计算
4、并发 vs 并行
并发:
时间片交替执行
单核CPU
看似同时运行A B C
--|--|--|
并行:
真正同时执行
多核CPU
实际同时运行A B C
-----
5、并发编程模型
同步模型:
- 阻塞
- 非阻塞
- 同步
- 异步
通信模型:
共享内存
消息传递
Actor模型
CSP模型
6、并发编程挑战
常见问题:
- 竞争条件
- 死锁
- 资源争用
- 线程安全
解决策略:
互斥锁
信号量
原子操作
无锁编程
7、代码示例
并发示例:
// 并发执行
void concurrentTask() {
std::thread t1([]{
// 任务A
});
std::thread t2([]{
// 任务B
});
t1.join();
t2.join();
}
并行示例:
// 并行计算
void parallelCompute(std::vector<int>& data) {
// 使用并行算法
std::for_each(
std::execution::par, // 并行执行
data.begin(),
data.end(),
[](int& value) {
value *= 2; // 并行处理每个元素
}
);
}
8、并发级别
进程级并发
线程级并发
指令级并行
数据级并行
9、应用场景
适合并发/并行:
科学计算
图形渲染
大数据处理
网络服务器
实时系统
10、性能考虑
性能影响因素:
- 任务granularity
- 通信开销
- 同步机制
- 硬件配置
优化策略:
- 最小化锁竞争
- 减少上下文切换
- 负载均衡
- 选择合适并发粒度
- 现代C++并发支持
标准库支持:
- <thread>
- <mutex>
- <condition_variable>
- <future>
- <atomic>
选择依据:
- 任务特性
- 硬件配置
- 性能需求
- 复杂度
建议:
- 优先考虑并发
- 评估并行收益
- 使用高级抽象
- 谨慎设计
二、代码实践
1、函数指针创建线程
#include <iostream>
#include <thread>
// 普通函数
void threadFunction(int param) {
std::cout << "Thread with param: " << param << std::endl;
}
int main() {
// 使用函数指针创建线程
std::thread t1(threadFunction, 42);
t1.join();
return 0;
}
2、多种函数指针类型
// 不同签名的函数指针
void simpleFunction() {
std::cout << "Simple function" << std::endl;
}
void paramFunction(int x, double y) {
std::cout << "Params: " << x << ", " << y << std::endl;
}
int main() {
// 无参函数
std::thread t1(simpleFunction);
// 多参数函数
std::thread t2(paramFunction, 10, 3.14);
t1.join();
t2.join();
}
3、函数指针与引用传递
void referenceFunction(int& value) {
value *= 2;
}
int main() {
int x = 5;
// 使用std::ref传递引用
std::thread t(referenceFunction, std::ref(x));
t.join();
std::cout << "Modified x: " << x << std::endl; // 输出10
}
4、类成员函数线程
class Worker {
public:
void memberFunction(int param) {
std::cout << "Member function: " << param << std::endl;
}
static void staticMemberFunction(int param) {
std::cout << "Static member function: " << param << std::endl;
}
};
int main() {
Worker worker;
// 成员函数线程
std::thread t1(&Worker::memberFunction, &worker, 100);
// 静态成员函数线程
std::thread t2(Worker::staticMemberFunction, 200);
t1.join();
t2.join();
}
5、函数指针与Lambda结合
int main() {
// 使用函数指针类型的Lambda
auto lambdaFunc = [](int x) {
std::cout << "Lambda thread: " << x << std::endl;
};
std::thread t(lambdaFunc, 42);
t.join();
// 动态函数指针
typedef void (*FuncPtr)(int);
FuncPtr ptr = lambdaFunc;
std::thread t2(ptr, 100);
t2.join();
}
6、高级函数指针线程池
#include <vector>
#include <functional>
#include <mutex>
class ThreadPool {
private:
std::vector<std::thread> threads;
std::mutex mutex;
public:
// 使用函数指针创建线程
void addThread(void (*func)(int), int param) {
std::lock_guard<std::mutex> lock(mutex);
threads.emplace_back(func, param);
}
// 使用std::function更灵活
void addThreadFunc(std::function<void()> func) {
std::lock_guard<std::mutex> lock(mutex);
threads.emplace_back(func);
}
void joinAll() {
for (auto& t : threads) {
if (t.joinable()) {
t.join();
}
}
}
};
// 示例函数
void workerFunc(int id) {
std::cout << "Worker " << id << " running" << std::endl;
}
int main() {
ThreadPool pool;
// 使用函数指针创建线程
pool.addThread(workerFunc, 1);
pool.addThread(workerFunc, 2);
// 使用Lambda
pool.addThreadFunc([]() {
std::cout << "Lambda thread" << std::endl;
});
pool.joinAll();
}
7、安全的函数指针线程管理
class SafeThread {
private:
std::unique_ptr<std::thread> thread;
std::atomic<bool> stop{false};
public:
template<typename Func, typename... Args>
void start(Func&& func, Args&&... args) {
// 确保之前的线程已结束
if (thread && thread->joinable()) {
thread->join();
}
// 创建新线程
thread = std::make_unique<std::thread>(
std::forward<Func>(func),
std::forward<Args>(args)...
);
}
void stop() {
stop = true;
if (thread && thread->joinable()) {
thread->join();
}
}
~SafeThread() {
stop();
}
};
8、注意事项
- 避免悬空指针
- 使用std::ref传递引用
- 注意生命周期管理
- 使用智能指针管理资源
- 考虑线程安全
9、性能建议
- 尽量减少线程创建开销
- 使用线程池
- 避免频繁创建和销毁线程
- 选择合适的线程数
10、错误处理
try {
std::thread t(threadFunction);
t.join();
} catch (const std::system_error& e) {
std::cerr << "Thread error: " << e.what() << std::endl;
}