在 C++11 中,std::thread
提供了对于多线程编程的强大支持。本节内容将详细探讨 std::thread
的使用,包括构造函数、成员函数、参数传递的注意事项等,同时结合示例进行深入讲解。
一、头文件
要使用 std::thread
,需要包含以下头文件:
cpp
#include <iostream> // 用于输入输出操作
#include <thread> // 用于线程支持
#include <mutex> // 用于线程间的同步(可选)
#include <vector> // 用于容器支持(可选)
#include <memory> // 用于智能指针(可选)
具体来说:
#include <iostream>
:用于输入输出操作,通常用于打印信息。#include <thread>
:这是使用std::thread
所必需的头文件,提供了多线程支持。#include <mutex>
:用于定义互斥量(std::mutex
)等同步机制,以控制线程之间的访问。#include <vector>
:常用于存储线程或者其他动态数组的容器,虽然不是std::thread
必需的,但在多线程编程中经常使用。#include <memory>
:提供智能指针(如std::shared_ptr
、std::unique_ptr
),在多线程环境中管理动态内存时非常有用。
二、std::thread
构造函数
std::thread
的构造函数用于创建一个新线程并执行指定的可调用对象(如函数、Lambda 表达式等)。
示例 1:基本的线程创建
cpp
#include <iostream>
#include <thread>
void threadFunction() {
std::cout << "Thread is running!" << std::endl;
}
int main() {
std::thread t(threadFunction); // 创建线程并执行 threadFunction
if (t.joinable()) { // 检查线程是否可以被加入
t.join(); // 等待线程完成
}
std::cout << "Thread has finished." << std::endl;
return 0;
}
输出结果
cpp
Thread is running!
Thread has finished.
三、其他成员函数
1. join()
用于等待线程完成。
2. joinable()
检查线程是否可以被加入。
3. detach()
将线程与主线程分离,允许线程在后台运行。
示例 2:使用 detach()
cpp
#include <iostream>
#include <thread>
#include <chrono>
void threadFunction() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Detached thread is running!" << std::endl;
}
int main() {
std::thread t(threadFunction);
t.detach(); // 使线程与主线程分离
std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待一段时间
std::cout << "Main thread is finishing." << std::endl;
return 0;
}
输出结果
cpp
Detached thread is running!
Main thread is finishing.
四、传递临时参数作为线程对象的注意事项
4.1 解决办法
在创建线程时传递临时参数时,需要注意临时对象的生命周期问题。
示例 3:传递临时参数错误示例
cpp
#include <iostream>
#include <thread>
void printMessage(std::string msg) {
std::cout << msg << std::endl;
}
int main() {
std::thread t(printMessage, "Hello from thread!"); // 传递临时参数
t.join(); // 等待线程完成
return 0;
}
输出结果
cpp
Hello from thread!
4.2 原因分析
虽然上述程序在此情况下运行正常,但如果将其改为使用临时对象的引用,则会出现问题:
示例 4:传递临时参数的引用错误示例
cpp
#include <iostream>
#include <thread>
void printMessage(const std::string& msg) {
std::cout << msg << std::endl;
}
int main() {
std::string temp = "Hello from thread!";
std::thread t(printMessage, std::move(temp)); // 使用 std::move 传递
t.join(); // 等待线程完成
// std::cout << temp << std::endl; // 这里会导致未定义行为
return 0;
}
4.3 总结
在创建线程时,避免直接传递临时对象的引用。可以使用 std::shared_ptr
或 std::unique_ptr
来确保对象的生命周期。
五、传递类对象、智能指针作为线程参数
5.1 修改子线程中的对象,不会影响主线程中的对象
如果向线程传递对象的拷贝,当在子线程中修改对象时,主线程中的对象不会受到影响。
示例 5:传递类对象
cpp
#include <iostream>
#include <thread>
class Counter {
public:
void increment() {
for (int i = 0; i < 5; ++i) {
++value;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int getValue() const {
return value;
}
private:
int value = 0;
};
int main() {
Counter counter;
std::thread t(&Counter::increment, counter); // 传递对象的拷贝
t.join(); // 等待线程完成
std::cout << "Main thread counter value: " << counter.getValue() << std::endl; // 值为 0
return 0;
}
输出结果
cpp
Main thread counter value: 0
5.2 传递智能指针
使用智能指针可以在多个线程间共享同一对象。
示例 6:使用 std::shared_ptr
cpp
#include <iostream>
#include <thread>
#include <memory>
class Counter {
public:
void increment() {
for (int i = 0; i < 5; ++i) {
++value;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
int getValue() const {
return value;
}
private:
int value = 0;
};
int main() {
auto counter = std::make_shared<Counter>(); // 创建共享指针
std::thread t(&Counter::increment, counter); // 传递智能指针
t.join(); // 等待线程完成
std::cout << "Main thread counter value: " << counter->getValue() << std::endl; // 值为 5
return 0;
}
输出结果
cpp
Main thread counter value: 5
六、std::this_thread
命名空间中相关辅助函数介绍
std::this_thread
命名空间提供了一些与当前线程相关的辅助函数。
1. sleep_for()
使当前线程睡眠指定的时间。
示例 7:使用 sleep_for()
cpp
#include <iostream>
#include <thread>
void threadFunction() {
std::cout << "Thread will sleep for 2 seconds..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2)); // 使线程睡眠
std::cout << "Thread awakens!" << std::endl;
}
int main() {
std::thread t(threadFunction);
t.join(); // 等待线程完成
return 0;
}
输出结果
cpp
Thread will sleep for 2 seconds...
Thread awakens!
2. sleep_until()
使当前线程睡眠直到指定的时间点。
示例 8:使用 sleep_until()
cpp
#include <iostream>
#include <thread>
#include <chrono>
void threadFunction() {
auto wakeUpTime = std::chrono::steady_clock::now() + std::chrono::seconds(2);
std::cout << "Thread will sleep until a specific time..." << std::endl;
std::this_thread::sleep_until(wakeUpTime); // 睡眠直到指定时间
std::cout << "Thread awakens!" << std::endl;
}
int main() {
std::thread t(threadFunction);
t.join(); // 等待线程完成
return 0;
}
输出结果
cpp
Thread will sleep until a specific time...
Thread awakens!
3. get_id()
获取当前线程的 ID。
示例 9:使用 get_id()
cpp
#include <iostream>
#include <thread>
void printThreadId() {
std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
}
int main() {
std::thread t(printThreadId);
t.join(); // 等待线程完成
return 0;
}
输出结果
cpp
Thread ID: 140736910476544
(线程 ID 会因运行环境而异。)
总结
本文详细探讨了 C++ 多线程编程中的 std::thread
,包括构造函数、成员函数、参数传递的注意事项、类对象和智能指针的使用,以及 std::this_thread
命名空间中的辅助函数。通过这些示例,希望能够帮助您更深入地理解 C++ 中的多线程编程,并在实际开发中灵活应用。