阅读导航
- 引言
- 一、thread类的简单介绍
- 二、thread类的用法
-
- [1. 创建线程](#1. 创建线程)
- [2. 使用 Lambda 表达式](#2. 使用 Lambda 表达式)
- [3. 传递参数给线程](#3. 传递参数给线程)
- [4. 线程的 join 和 detach](#4. 线程的 join 和 detach)
- [5. 检查线程是否可 join](#5. 检查线程是否可 join)
- [6. 线程的 ID](#6. 线程的 ID)
- [7. 线程的移动语义](#7. 线程的移动语义)
- [8. 线程的析构](#8. 线程的析构)
- [🚨 注意事项](#🚨 注意事项)
- 三、线程函数参数
- 温馨提示
引言
C++ thread线程库是C++11标准引入的一个强大工具,它提供了一种便捷的方式来创建和管理线程,使得并行编程变得更加容易和高效。这个库支持线程的创建、同步、互斥以及线程局部存储等功能。通过使用std::thread
类,开发者可以轻松地创建新的线程来执行任务,并使用join()
方法来等待线程完成。此外,线程库还包括了std::mutex
和std::lock_guard
等同步原语,以帮助管理线程间的资源访问,防止数据竞争和死锁。线程局部存储std::thread::id
和thread_local
关键字则允许线程拥有自己的局部数据,这在多线程环境中非常有用。总的来说,C++ thread线程库为C++开发者提供了一个功能全面、易于使用的多线程编程解决方案。让我们一起开始这段关于thread
线程库的学习之旅吧。
一、thread类的简单介绍
std::thread
类是C++11标准库中的一个核心组件,用于创建和管理独立的线程。它允许开发者通过传递一个函数或可调用对象来初始化线程,执行并行任务。线程对象的生命周期控制着线程的执行,而通过join()
和detach()
方法,可以控制线程的同步和分离。此外,std::thread
还提供了线程ID和状态检查功能,帮助开发者进行线程管理和异常处理,确保程序的稳定性和效率。
🚨注意 :要使用线程库中的线程,必须包含<thread>头文件 。线程类官方介绍文档
下面这个表格包含了 std::thread
类的构造函数、赋值运算符、比较运算符以及一些用于线程管理的成员函数。这些函数提供了创建、管理、比较和销毁线程的能力.
函数名 | 功能描述 |
---|---|
id get_id() | 返回线程的唯一标识符。 |
bool joinable() | 检查线程是否可 join,即是否还在运行。 |
void join() | 等待线程结束执行。 |
void detach() | 将线程与 std::thread 对象分离,使其在后台独立运行。 |
void swap(std::thread& other) | 与另一个 std::thread 对象交换线程。 |
thread::native_handle_type native_handle() | 返回线程的原生句柄,用于操作系统特定的线程操作。 |
bool operator==(const thread& other) const | 比较两个线程是否相同。 |
bool operator!=(const thread& other) const | 比较两个线程是否不同。 |
thread() noexcept | 默认构造函数,创建一个未关联线程的 std::thread 对象。 |
thread(nullptr_t) noexcept | 构造一个未关联线程的 std::thread 对象。 |
explicit thread(Callable&& func, Args&&... args) | 构造函数,创建一个线程并启动它来执行给定的可调用对象和参数。 |
thread(thread&& other) noexcept | 移动构造函数,获取另一个 std::thread 对象的所有权。 |
thread& operator=(thread&& other) noexcept | 移动赋值运算符,获取另一个 std::thread 对象的所有权。 |
~thread() | 析构函数,如果线程可 join,则会调用 join() ,否则调用 detach() 。 |
二、thread类的用法
std::thread
类是 C++ 标准库中用于线程创建和管理的类。以下是 std::thread
类的一些关键用法和示例:
1. 创建线程
要创建一个线程,你需要实例化 std::thread
对象并传递一个函数或可调用对象(如 lambda 表达式或函数对象)作为参数。
cpp
#include <iostream>
#include <thread>
void threadFunction() {
std::cout << "Hello from thread!" << std::endl;
}
int main() {
std::thread t(threadFunction);
t.join(); // 等待线程结束
return 0;
}
2. 使用 Lambda 表达式
Lambda 表达式提供了一种简洁的方式来定义匿名函数对象,非常适合用于线程。
cpp
#include <iostream>
#include <thread>
int main() {
std::thread t([]() {
std::cout << "Hello from lambda thread!" << std::endl;
});
t.join();
return 0;
}
3. 传递参数给线程
你可以将参数传递给线程函数。
cpp
void threadFunctionWithArgs(int x, double y) {
std::cout << "x: " << x << ", y: " << y << std::endl;
}
int main() {
std::thread t(threadFunctionWithArgs, 10, 3.14);
t.join();
return 0;
}
4. 线程的 join 和 detach
join()
: 调用此方法会阻塞,直到线程结束执行。detach()
: 调用此方法会使线程在后台继续运行,而不受std::thread
对象的生命周期限制。
cpp
int main() {
std::thread t([]() {
// 线程执行的代码
});
t.detach(); // 线程现在在后台运行,不会等待它结束
// 主线程继续执行,而 t 线程在后台运行
return 0;
}
5. 检查线程是否可 join
在调用 join()
或 detach()
之前,可以使用 joinable()
检查线程是否还在运行。
cpp
int main() {
std::thread t([]() {
// 线程执行的代码
});
if (t.joinable()) {
t.join(); // 线程还在运行,等待结束
}
return 0;
}
6. 线程的 ID
每个线程都有一个唯一的 ID,可以使用 get_id()
获取。
cpp
int main() {
std::thread t([]() {
std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;
});
t.get_id(); // 获取线程 ID
t.join();
return 0;
}
7. 线程的移动语义
std::thread
对象可以被移动,但不能被复制。这意味着你可以通过移动语义来转移线程的所有权。
cpp
int main() {
std::thread t1([]() {
std::cout << "Thread 1 running" << std::endl;
});
std::thread t2 = std::move(t1); // t1 的所有权转移给 t2
t2.join(); // 等待线程结束
return 0;
}
8. 线程的析构
当 std::thread
对象被销毁时,如果线程是可 join 的,那么 join()
会被自动调用。如果线程已经被分离,则不会有任何操作。
cpp
{
std::thread t([]() {
// 线程执行的代码
});
// t 在这里离开作用域,自动调用 join()
} // t 的析构函数被调用
🚨 注意事项
- 确保在
std::thread
对象生命周期结束前,线程已经被正确处理(join 或 detach)。 - 避免在线程函数中调用
std::exit()
或者抛出未捕获的异常,因为这可能会导致程序的不稳定。 - 使用互斥锁和条件变量来同步线程,避免数据竞争和死锁。
std::thread
提供了强大的工具来实现多线程编程,但也需要谨慎使用以确保程序的正确性和稳定性。
三、线程函数参数
线程函数的参数是以值拷贝的方式拷贝到线程栈空间中的 ,因此:即使线程参数为引用类型,在线程中修改后也不能修改外部实参,因为其实际引用的是线程栈中的拷贝,而不是外部实参。
cpp
#include <thread>
void ThreadFunc1(int& x) {
x += 10;
}
void ThreadFunc2(int* x) {
*x += 10;
}
int main() {
int a = 10;
// 在线程函数中对a修改,不会影响外部实参,因为:线程函数参数虽然是引用方式,但其实际引用的是线程栈中的拷贝
std::thread t1(ThreadFunc1, a);
t1.join();
// 如果想要通过形参改变外部实参时,必须借助std::ref()函数
std::thread t2(ThreadFunc1, std::ref(a));
t2.join(); // 等待线程执行完成
// 地址的拷贝
std::thread t3(ThreadFunc2, &a);
t3.join(); // 等待线程执行完成
return 0;
}
温馨提示
感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!
再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!