【C++多线程】thread
C++中的std::thread
是C++11引入的线程库的一部分,提供了创建和管理线程的能力。它封装了操作系统的线程接口,使得在C++中更方便地进行多线程编程。
1. std::thread
的定义
std::thread
类位于<thread>
头文件中,定义在std
命名空间下,用于表示一个独立执行的线程对象。其基本声明如下:
#include <thread>
std::thread 可以传递各种类型的参数,但要注意:
默认按值传递,如果要传引用,用 std::ref(x)。
移动对象用 std::move,避免不必要的拷贝。
成员函数必须传对象指针(&obj)。
2. std::thread
的构造函数
std::thread
提供了多个构造函数,允许不同方式创建线程。
(1) 默认构造函数
std::thread();
- 创建一个空的
std::thread
对象,不与任何可执行线程关联。
#include <iostream>
#include <thread>
int main() {
std::thread t; // 默认构造,不与任何线程关联
std::cout << "t is joinable? " << t.joinable() << std::endl; // 输出 0(false)
return 0;
}
(2) 可调用对象构造
template< class Function, class... Args >
explicit thread(Function&& f, Args&&... args);
- 传入一个可调用对象(函数、函数对象、lambda 等)和参数,创建一个新的线程。
#include <iostream>
#include <thread>
void func(int x) {
std::cout << "Thread function with arg: " << x << std::endl;
}
int main() {
std::thread t(func, 10); // 创建线程并执行 func(10)
t.join(); // 等待线程执行完毕
return 0;
}
(3) 移动构造函数
thread(thread&& other) noexcept;
std::thread
是不可拷贝的,但可以移动。- 该构造函数接收另一个
std::thread
对象,并接管其线程。
#include <iostream>
#include <thread>
void func() {
std::cout << "Thread running\n";
}
int main() {
std::thread t1(func);
std::thread t2 = std::move(t1); // t1移动到t2,t1不再管理线程
t2.join();
return 0;
}
(4) 拷贝构造(删除)
thread(const thread&) = delete;
std::thread
对象不能被拷贝,因为线程的所有权是唯一的。- 这样设计是为了避免多个
std::thread
对象管理同一个线程,导致未定义行为。
#include <thread>
void func() {}
int main() {
std::thread t1(func);
// std::thread t2 = t1; // ❌ 错误,不能拷贝
return 0;
}
3. std::thread
的常用成员函数
(1) join()
- 等待线程结束
void join();
- 阻塞调用线程,直到目标线程执行完毕。
- 必须在可联结的线程上调用,否则会导致异常。
#include <iostream>
#include <thread>
void func() {
std::cout << "Thread running\n";
}
int main() {
std::thread t(func);
t.join(); // 等待线程执行完毕
return 0;
}
(2) detach()
- 分离线程
void detach();
- 让线程在后台运行,与
std::thread
对象分离。 - 线程对象会立即销毁,但线程继续执行,执行完毕后自动释放资源。
#include <iostream>
#include <thread>
#include <chrono>
void func() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Thread finished\n";
}
int main() {
std::thread t(func);
t.detach(); // 线程进入后台
std::cout << "Main thread continues...\n";
std::this_thread::sleep_for(std::chrono::seconds(3)); // 主线程等待
return 0;
}
注意:如果主线程退出而分离线程还未执行完,可能会导致未定义行为。因此应谨慎使用detach()
。
(3) joinable()
- 判断线程是否可联结
bool joinable() const noexcept;
- 如果线程对象管理一个有效线程,则返回
true
。
std::thread t(func);
if (t.joinable()) {
t.join();
}
(4) get_id()
- 获取线程ID
std::thread::id get_id() const noexcept;
- 获取线程的唯一ID。
#include <iostream>
#include <thread>
void func() {
std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
}
int main() {
std::thread t(func);
t.join();
return 0;
}
(5) native_handle()
- 获取操作系统的线程句柄
native_handle_type native_handle();
- 获取线程的原生句柄,可用于底层线程操作(如设置优先级等)。
(6) hardware_concurrency()
- 获取硬件支持的线程数
static unsigned int hardware_concurrency() noexcept;
- 返回系统建议的并发线程数。
#include <iostream>
#include <thread>
int main() {
std::cout << "Hardware concurrency: " << std::thread::hardware_concurrency() << std::endl;
return 0;
}
4. std::thread
的用法示例
(1) 使用Lambda表达式
#include <iostream>
#include <thread>
int main() {
std::thread t([] {
std::cout << "Lambda thread running\n";
});
t.join();
return 0;
}
(2) 线程数组
#include <iostream>
#include <thread>
#include <vector>
void func(int i) {
std::cout << "Thread " << i << " started\n";
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 5; ++i) {
threads.emplace_back(func, i);
}
for (auto& t : threads) {
t.join();
}
return 0;
}
总结
std::thread
不能拷贝,但可以移动。- 使用
join()
等待线程结束,使用detach()
分离线程。 - 使用
joinable()
检查线程是否可联结。 - 使用
get_id()
获取线程ID。 hardware_concurrency()
获取系统支持的线程数。
多线程编程需要注意数据同步,否则可能导致数据竞争(race condition)等问题。可以使用std::mutex
、std::condition_variable
等同步机制来解决这些问题。