C++ 多线程编程
近期博主在RK3588上部署YOLO模型,遇到了NPU调度问题,由于RK3588NPU簇有三颗核心,为了充分利用
(压榨)这三颗核心,博主查阅资料,发现 C++11 引入了对多线程编程的原生支持,使得在 C++ 中创建和管理线程变得更加简单和高效。本文将介绍 C++ 多线程编程的基本概念、常用库以及一些示例代码。
<thread>的是什么?
<thread> 是 C++11 标准库中的一个头文件,提供了对多线程编程的支持。它定义了 std::thread 类,用于创建和管理线程。通过 <thread>,程序员可以轻松地创建多个线程来并行执行任务,从而提高程序的性能和响应能力。
线程创建
1. 使用 std::thread 创建线程
#include <iostream>
#include <thread>
void hello() {
std::cout << "Hello Concurrent World\n";
}
int main(){
std::thread t(hello);
t.join();
}好的!看来你成功使用多线程了。先做点什么呢?启动线程、结束线程,还是如何监管线程?
2. 使用 std::async 创建线程
#include <iostream>
#include <future>
int threadFunc(int n) {
return n * n;
}
int main() {
std::future<int> result = std::async(threadFunc, 2); // 创建线程,执行 threadFunc 函数,并返回一个 future 对象
std::cout << "Result: " << result.get() << std::endl; // 获取线程返回值
return 0;
}线程同步
1. 使用互斥锁 std::mutex 进行线程同步
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void threadFunc(int n) {
mtx.lock(); // 加锁
std::cout << "Thread " << n << " is running" << std::endl; // 临界区
mtx.unlock(); // 解锁
}
int main() {
std::thread t1(threadFunc, 1);
std::thread t2(threadFunc, 2);
t1.join();
t2.join();
return 0;
}注意:请勿创建和启动全局线程,这会导致程序崩溃。
生命周期管理失控:线程 my_thread 是一个全局对象。它的生命周期与整个程序相同。当 main 函数执行完毕,程序准备退出时,全局对象 my_thread 会被销毁(调用其析构函数)。
程序崩溃:stdthread 对象在销毁时仍然是可 joinable 的(即它关联的线程还在运行且没有被 join() 或 detach()),程序会立即调用 std::terminate(),导致程序异常终止。
无法 join:由于 my_thread 是全局的,我们没有合适的地方去调用 my_thread.join() 来等待它结束。main 函数结束时调用已经太晚了。
创建线程:使用命名对象或 Lambda 表达式。(避免 most vexing parse )
#include <iostream>
#include <thread>
void threadFunc(int n) {
std::cout << "Thread " << n << " is running" << std::endl;
}
int main() {
std::thread t1(threadFunc, 1); // 命名对象
std::thread t2([](int n) { // Lambda 表达式
std::cout << "Thread " << n << " is running" << std::endl;
}, 2);
t1.join();
t2.join();
return 0;
}最佳实践:在函数内部创建线程,并确保在对象销毁前调用 join() 或 detach()。对于简单的任务,优先考虑 Lambda 表达式。对于复杂的任务,创建一个命名对象或函数再传递给线程。
什么是 Lambda 表达式?Lambda 表达式是 C++11 引入的一种匿名函数,它允许在需要函数对象的地方直接定义一个函数。如果你写过js,你会理解。在这里不过多赘述,感兴趣的读者可以查阅相关资料。
