文章目录
- 前言
- 一、多线程
-
- 1.多线程使用
- 2.多线程参数传递的注意事项
- 3.多线程常用函数
-
- 1.std::this_thread命名空间
- [2.hardware_concurrency cpu核心数计算](#2.hardware_concurrency cpu核心数计算)
- 二、线程的同步
-
- 1.mutex互斥量
- [2.使用锁管理类 std::lock_guard 和 std::unique_lock](#2.使用锁管理类 std::lock_guard 和 std::unique_lock)
- 3.condition_variable
- 4.atomic原子操作
- 三、异步并发
- 结束语
- 💂 个人主页 :风间琉璃
- 🤟 版权 : 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
- 💬 如果文章对你有
帮助
、欢迎关注
、点赞
、收藏(一键三连)
和订阅专栏
哦
前言
提示:这里可以添加本文要记录的大概内容:
在 C++11 之前,C++ 标准库并未提供内建的多线程支持。要在 C++ 中实现多线程,程序员需要依赖操作系统提供的原生线程库,如在 Linux 中使用 <pthread.h>,或在 Windows 中使用 <windows.h> 等平台特定的 API。
C++11提供了语言层面上的多线程,包含在头文件中。它解决了跨平台的问题,提供了管理线程、保护共享数据、线程间同步操作、原子操作等类。
一、多线程
1.多线程使用
C++11中提供的线程类叫做std::thread
,基于这个类创建一个新的线程只需要提供线程函数 或者函数对象即可,并且可以同时指定线程函数的参数。
cpp
// 1
thread() noexcept;
// 2
thread( thread&& other ) noexcept;
// 3
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
// 4
thread( const thread& ) = delete;
-
构造函数①:默认构造函数,构造一个线程对象,在这个线程中不执行任何处理动作
-
构造函数②:移动构造函数,将 other 的线程所有权转移给新的thread 对象。之后 other 不再表示执行线程。
-
构造函数③:
创建线程对象
,并在该线程中执行函数f
中的业务逻辑,args是要传递给函数f的参数
任务函数f的可选类型有很多,具体如下:
普通函数
,类成员函数
,匿名函数
,仿函数
(这些都是可调用对象类型)- 可以是可调用
对象包装器类型
,也可以是使用绑定器绑定之后得到的类型(仿函数)
-
构造函数④:使用=delete显示删除拷贝构造, 不允许线程对象之间的拷贝
使用示例:
cpp
#include <iostream>
#include <thread> // c++ 多线程库
// 线程函数
void func(int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << std::this_thread::get_id() <<" Thread Function executing: " << i << std::endl;
}
}
int main()
{
int n = 10;
std::thread thread1(func,n); // 创建线程,执行 func 函数
thread1.join(); // 等待线程结束
// 主线程继续执行
for (int i = 0; i < 5; ++i)
{
std::cout << std::this_thread::get_id() << " Main Thread executing: " << i << std::endl;
}
return 0;
}
应用程序启动后默认只有一个线程,这个线程一般称为主线程或父线程
,通过线程类创建出的线程一般称为子线程 ,每个被创建出的线程实例都对应一个线程ID
,这个ID是唯一的,可以通过ID来区分和识别各个已经存在的线程实例,获取线程ID的函数叫做get_id(),函数原型如下:
cpp
std::thread::id get_id() const noexcept;
thread1.get_id() // 线程对象调用get_id()获取子线程ID
std::thread thread1(func,n)创建子线程对象thread1,func()函数会在子线程中运行,这是一个回调函数,线程对象创建后就会执行任务函数
。
func()的参数是通过thread的参数进行传递的,线程类的构造函数第三种是一个变参函数
,因此无需担心线程任务函数的参数个数问题。任务函数func()一般返回值指定为void
,因为子线程在调用这个函数的时候不会处理其返回值。
当线程启动后,一定要在和线程相关联的thread销毁(std::terminate()
)前,确定以何种方式等待线程执行结束,回收线程所使用的资源。程序必须要在线程对象销毁之前在二者之间作出选择,否则程序运行期间就会有bug产生。thread库提供了两种解决方案:
- 加入式(
join()
):等待启动的线程完成,才会继续往下执行 - 分离式(
detach()
):启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束
1.join()线程回收
join()
阻塞当前线程,直到被调用的线程完成执行。在某个线程中通过子线程对象调用join()函数,调用这个函数的线程被阻塞,但是子线程对象中的任务函数会继续执行 ,当任务执行完毕后,join()会清理当前子线程中的相关资源然后返回
,同时调用该函数的线程解除阻塞继续向下执行。
cpp
void join();
2.detach()线程分离
detach()
函数的作用是进行线程分离,分离主线程和创建出的子线程
。启动的线程自主在后台运行,在线程分离后,主线程退出也会一并销毁创建出的所有子线程 ,但是在主线程退出前,它可以脱离主线程继续独立的运行,任务执行完毕后,这个子线程会自动释放自己占用的系统资源。
cpp
void detach();
注意事项:线程分离函数detach()不会阻塞线程,子线程和主线程分离后,在主线程中就不能再对这个子线程做任何控制,比如:通过join()阻塞主线程等待子线程中的任务执行完毕,或者调用get_id()获取子线程的线程ID。
3.joinable()
joinable()函数用于判断主线程和子线程是否处理关联(连接)状态,一般情况下,二者之间的关系处于关联状态,该函数返回一个布尔类型:
cpp
bool joinable() const noexcept;
可以使用joinable判断是join模式还是detach模式:
cpp
if (thread1.joinable())
{
thread1.join();
}
2.多线程参数传递的注意事项
参数传递有传值、传引用和传对象三种方式。
1.传值
:传递简单的参数类型(如整数)时,通常是按值传递。这样做可以避免数据竞争,但对于较大的数据结构,可能会引入不必要的拷贝开销。
cpp
void threadFunction(int n) {
// 使用 n
}
std::thread t(threadFunction, 5); // 按值传递
2.传引用
:如果需要传递较大的数据结构 或希望在线程间共享数据 ,可以按引用传递 。需要使用 std::ref 包装参数
以明确传递引用。
cpp
void threadFunction(std::vector<int>& vec) {
// 修改 vec
}
std::vector<int> data = {1, 2, 3, 4, 5};
std::thread t(threadFunction, std::ref(data)); // 按引用传递
3.传递对象
:传递对象时,需要考虑对象的拷贝构造函数和移动构造函数是否高效。如果对象不可拷贝或移动,可以考虑使用指针或智能指针。
cpp
class MyClass {
public:
void operator()() {
// 执行任务
}
};
MyClass obj;
std::thread t(std::move(obj)); // 使用 std::move 传递对象
3.多线程常用函数
1.std::this_thread命名空间
std::this_thread 是 C++11 标准库中的一个命名空间,包含了一组用于与当前线程进行交互的函数。这些函数提供了一些基本的线程管理功能,如获取线程ID
、使线程休眠
等。
1.std::this_thread::get_id
std::this_thread::get_id
返回当前线程的 std::thread::id,用于唯一标识当前线程。
2.std::this_thread::sleep_for
std::this_thread::sleep_for
使当前线程休眠指定的时间
。调用该函数的线程会马上从运行态
变成阻塞态
并在这种状态下休眠一定的时长,因为阻塞态的线程已经让出CPU资源,代码也不会被执行。
cpp
#include <iostream>
#include <thread>
#include <chrono>
void doWork() {
std::cout << "Work started..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Work finished after 2 seconds." << std::endl;
}
int main() {
std::thread t(doWork);
t.join();
return 0;
}
需要注意的是:程序休眠完成后,会从阻塞态重新变成就绪态 ,就绪态的线程需要再次争抢CPU时间片,抢到后才会变成运行态,这时候程序才会继续向下运行。
3.std::this_thread::sleep_until
std::this_thread::sleep_until 使当前线程休眠直到指定的时间点。
cpp
#include <iostream>
#include <thread>
#include <chrono>
void doWork() {
std::cout << "Work started..." << std::endl;
auto wakeUpTime = std::chrono::steady_clock::now() + std::chrono::seconds(2);
std::this_thread::sleep_until(wakeUpTime);
std::cout << "Work finished after 2 seconds." << std::endl;
}
int main() {
std::thread t(doWork);
t.join();
return 0;
}
4.std::this_thread::yield
std::this_thread::yield 提示操作系统可以将当前线程的执行让位给另一个线程
。这个函数不会阻塞当前线程,但它可以帮助更好地调度线程。
cpp
#include <iostream>
#include <thread>
void busyWork() {
for (int i = 0; i < 5; ++i) {
std::cout << "Doing busy work " << i << std::endl;
std::this_thread::yield(); // Yield to allow other threads to run
}
}
int main() {
std::thread t(busyWork);
t.join();
return 0;
}
- std::this_thread::yield() 的目的是
避免一个线程长时间占用CPU资源
,从而导致多线程处理性能下降 - std::this_thread::yield() 是让当前线程主动放弃了当前自己抢到的CPU资源,但是在下一轮还会继续抢
2.hardware_concurrency cpu核心数计算
thread线程类还提供了一个静态方法hardware_concurrency()
,用于获取当前计算机的CPU核心数
,根据这个结果在程序中创建出数量相等的线程,每个线程独自占有一个CPU核心,这些线程就不用分时复用CPU时间片,此时程序的并发效率是最高的。
cpp
static unsigned hardware_concurrency() noexcept;
int num = std::thread::hardware_concurrency();
二、线程的同步
只读数据在线程间操作时是安全稳定的,不需要特殊处理,直接读即可。然而,当一些线程进行写操作,而另一些线程进行读操作时,如果不进行特殊处理,程序肯定会崩溃。
为了解决这个问题,针对读写操作,需要在对某个数据进行读写时先进行加锁
。其他线程必须等待该操作完成并对数据解锁
后,才能进行访问。这样可以保护共享数据,确保线程安全。
1.mutex互斥量
在 C++11 中,互斥量(mutex)是一种用于管理对共享资源访问的同步原语。mutex提供了4种互斥类型:
类型 | 作用 |
---|---|
std::mutex互斥量 | 基本的Mutex类 |
std::recursive_mutex递归互斥量 | 递归互斥量允许同一线程多次锁定同一互斥量 |
std::timed_mutex定时互斥量 | 定时互斥量允许尝试锁定互斥量,并在指定时间内进行等待 |
std::recursive_timed_mutex递归定时互斥量 | 递归定时互斥量结合了递归互斥量和定时互斥量的特性 |
a.构造函数
std::mutex:默认构造函数,创建一个未锁定的互斥量对象
cpp
std::mutex mtx;
b.上锁函数
lock
:锁定互斥量。如果互斥量已经被锁定,则阻塞当前线程,直到互斥量变为可用。
cpp
mtx.lock();
c.解锁函数
unlock
:解锁互斥量。如果当前线程没有锁定互斥量,行为未定义。
cpp
mtx.unlock();
d.尝试上锁函数
try_lock:尝试锁定互斥量。如果成功锁定,则返回 true;如果互斥量已经被锁定,则立即返回 false。
cpp
if (mtx.try_lock()) {
// 锁定成功
} else {
// 锁定失败
}
示例
cpp
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
std::mutex mtx; // 定义一个互斥锁
int shared_counter = 0; // 共享数据
void increment(int id) {
for (int i = 0; i < 100; ++i) {
mtx.lock(); // 显式加锁
++shared_counter;
std::cout << "Thread " << id << " incremented counter to " << shared_counter << std::endl;
mtx.unlock(); // 显式解锁
}
}
int main() {
const int num_threads = 10;
std::vector<std::thread> threads;
// 创建多个线程
for (int i = 0; i < num_threads; ++i) {
threads.push_back(std::thread(increment, i));
}
// 等待所有线程完成
for (auto& t : threads) {
t.join();
}
std::cout << "Final counter value: " << shared_counter << std::endl;
return 0;
}
2.使用锁管理类 std::lock_guard 和 std::unique_lock
1.std::lock_guard
std::lock_guard
是一种简单的 RAII(资源获取即初始化)类型,用于在作用域内自动管理互斥量的锁定和解锁
。
使用std::lock_guard实现自动加锁和解锁。当lock对象创建时,mtx被加锁;当lock对象超出作用域时,mtx自动解锁。这种方式确保了异常安全,防止在函数因异常退出时未能正确解锁的问题。
lock_guard的特点:
- 1.当构造函数被调用时,该互斥量会被自动锁定
- 2.当析造函数被调用时,该互斥量会被自动解锁
- 3.std::lock_guard对象
不能复制或移动
,因此它只能在局部作用域
中使用。
创建 std::lock_guard 对象并锁定互斥量
cpp
std::lock_guard<std::mutex> guard(mtx);
示例
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int counter = 0;
void increaseCounter() {
std::lock_guard<std::mutex> lock(mtx); // 锁定互斥量
++counter;
std::cout << "Counter: " << counter << std::endl;
// 离开作用域时,lock_guard 自动解锁
}
int main() {
std::thread t1(increaseCounter);
std::thread t2(increaseCounter);
t1.join();
t2.join();
return 0;
}
在这个示例中,increaseCounter 函数通过 std::lock_guard 锁定互斥量 mtx。当 increaseCounter 函数结束时,std::lock_guard 对象离开作用域,自动解锁 mtx。
2.std::unique_lock
std::unique_lock
提供了更灵活的锁管理功能,支持延迟锁定、手动解锁和重新锁定。unique_lock 是 lock_guard 的升级加强版,它具有 lock_guard 的所有功能,同时又具有其他很多方法,使用起来更强灵活方便,能够应对更复杂的锁定需要。
std::unique_lock
的特点包括:
- 延迟锁定 :在创建
std::unique_lock
对象时,可以通过指定第二个参数为std::defer_lock
来创建一个未锁定的互斥量对象,允许在需要时再锁定。 - 灵活性 :可以在对象的生命周期内
随时进行加锁和解锁操作
,提供了更大的灵活性。 - 自动释放锁 :与
std::lock_guard
类似,std::unique_lock
遵循 RAII 规则,在其作用域结束时自动释放锁。 - 不可复制,可移动 :
std::unique_lock
对象是不可复制的,但可以移动,从而支持在需要时转移锁的所有权。 - 条件变量支持 :在使用条件变量(如
std::condition_variable
)时,必须使用std::unique_lock
作为参数,以确保线程安全的等待和通知机制。
创建 std::unique_lock 对象,可以选择是否立即锁定互斥量
。
cpp
std::unique_lock<std::mutex> lock(mtx); // 使用给定的互斥量mtx进行初始化,并对该互斥量进行加锁操作
cpp
std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 不立即锁定
lock.lock(); // 手动锁定
使用示例
cpp
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
int counter = 0;
void increaseCounter() {
std::unique_lock<std::mutex> lock(mtx); // 锁定互斥量
++counter;
std::cout << "Counter: " << counter << std::endl;
// 离开作用域时,unique_lock 自动解锁
}
int main() {
std::thread t1(increaseCounter);
std::thread t2(increaseCounter);
t1.join();
t2.join();
return 0;
}
3.condition_variable
条件变量是允许多个线程相互交流的同步原语。它允许一定量的线程等待(可以定时)另一线程的提醒,然后再继续。条件变量始终关联到一个互斥。它提供了一种机制,允许一个或多个线程等待某个条件变量的改变,然后继续执行
。这对于解决生产者-消费者问题、实现事件通知等场景非常有用。
std::condition_variable 类是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量(条件)并通知 condition_variable 。当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。通过成员函数wait、notify_one、notify_all来进行条件相关的操作。
成员函数 | 作用 |
---|---|
wait() | 阻塞当前线程,直到被通知(通过 notify_one 或 notify_all),并且指定的条件为真 |
notify_one() | 通知一个等待该条件变量的线程,使其从 wait 中返回 |
notify_all() | 通知所有等待该条件变量的线程,使它们从 wait 中返回 |
wait函数有以下几种
cpp
void wait(std::unique_lock<std::mutex>& lock);
template< class Predicate >
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);
作用都是使当前线程进入等待状态,直到条件变量被通知。等待时,互斥锁会被解锁。当线程被唤醒后,互斥锁会被重新锁定。
第二种多了一个pred
: 一个谓词(通常是一个函数或 lambda 表达式
),返回 true 表示条件满足,线程不再等待 。用于防止虚假唤醒 ,即线程在条件满足前被意外唤醒。这个表达式在每次被唤醒时都会被检查,如果条件不满足,线程会重新进入等待状态。
使用步骤:
- 1.创建一个条件变量对象
std::condition_variable
。 - 2.创建一个
互斥锁 std::mutex
以保护共享数据。 - 3.在需要等待条件的线程中使用 std::unique_lock<std::mutex> lock锁定互斥锁,然后调用 wait(lock)。
- 4.在改变共享数据的线程中修改数据并调用 notify_one() 或 notify_all()。
示例
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
std::queue<int> dataQueue;
std::mutex mtx;
std::condition_variable cv;
bool finished = false;
void producer(int count) {
for (int i = 0; i < count; ++i) {
std::unique_lock<std::mutex> lock(mtx);
dataQueue.push(i);
std::cout << "Produced: " << i << std::endl;
cv.notify_one(); // 通知一个等待的消费者
}
std::unique_lock<std::mutex> lock(mtx);
finished = true;
cv.notify_all(); // 通知所有等待的消费者,生产结束
}
void consumer() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !dataQueue.empty() || finished; });
while (!dataQueue.empty()) {
int value = dataQueue.front();
dataQueue.pop();
std::cout << "Consumed: " << value << std::endl;
}
if (finished) break;
}
}
int main() {
std::thread prodThread(producer, 10);
std::thread consThread(consumer);
prodThread.join();
consThread.join();
return 0;
}
生产者线程:向队列中添加数据,每次添加数据后,调用 cv.notify_one() 通知消费者线程有新数据可用。生产完成后,将 finished 标志设置为 true,并调用 cv.notify_all() 通知所有消费者线程生产已完成。
消费者线程:等待数据队列非空或生产结束。使用 cv.wait() 进行等待,并传入一个 lambda 表达式作为条件,当队列非空或生产结束时解除等待,然后消费数据,直到队列为空并且生产结束。
4.atomic原子操作
std::atomic 提供了一组用于执行原子操作的模板和类型,以确保多线程编程中的数据安全。原子操作是不可分割的,不会被其他线程中断 ,在多线程环境下,多个线程同时对同一个 std::atomic
变量进行操作,可以避免数据竞争和不一致性。
cpp
#include <atomic>
std::atomic<int> atomicInt(0); // 声明一个原子整数并初始化为 0
示例
cpp
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
std::atomic<int> counter(0); // 声明一个原子计数器并初始化为 0
void incrementCounter(int numIterations) {
for (int i = 0; i < numIterations; ++i) {
//counter.fetch_add(1); // 原子加法操作
counter++;
}
}
int main() {
const int numThreads = 10;
const int numIterations = 1000;
std::vector<std::thread> threads;
// 创建并启动多个线程
for (int i = 0; i < numThreads; ++i) {
threads.push_back(std::thread(incrementCounter, numIterations));
}
// 等待所有线程完成
for (auto& t : threads) {
t.join();
}
std::cout << "Final counter value: " << counter << std::endl;
return 0;
}
三、异步并发
1.async与future
std::async 和 std::future 是 C++11 引入的标准库工具,用于实现异步任务和并发编程
。它们提供了一种简单的方式来启动异步任务,并获取任务的结果,而不需要显式地管理线程。
std::async
是一个模板函数,用于启动异步任务
。它会启动一个新的线程来执行指定的任务,并返回一个 std::future 对象
,用于获取任务的结果。
cpp
template< class Function, class... Args >
std::future< typename std::result_of<Function(Args...)>::type >
async( std::launch policy, Function&& f, Args&&... args );
- policy:指定任务的启动策略,可以是 std::launch::async(强制异步)或std::launch::deferred(延迟执行)。
- f:要执行的函数。
- args:传递给函数的参数。
- 返回一个 std::future 对象,可以用于获取异步任务的结果。
std::future 是一个模板类,用于访问异步操作的结果 。它可以从 std::async、std::promise 或 std::packaged_task
中获取。
主要方法
- get():获取异步操作的结果,
如果结果还未准备好,则阻塞等待
。 - wait():等待异步操作完成。
- valid():检查 std::future 是否有与共享状态相关联。
示例
cpp
#include <iostream>
#include <future>
#include <chrono>
// 异步任务函数
int asyncTask(int x) {
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟长时间任务
return x * x;
}
int main() {
// 使用 std::async 启动异步任务, 相当于创建了一个线程,不会阻塞程序
std::future<int> result = std::async(std::launch::async, asyncTask, 5);
// 主线程可以执行其他工作
std::cout << "Doing other work in main thread...\n";
// 获取异步任务结果,get()函数会阻塞程序直到异步操作执行完成
std::cout << "Result from asyncTask: " << result.get() << std::endl;
return 0;
}
2.packaged_task
std::packaged_task 的主要功能是将可调用对象(如函数、lambda 表达式或函数对象)包装成一个任务,并将其结果传递给一个 std::future 对象
。这样,任务可以在不同的线程中执行,并且主线程可以通过 std::future 获取任务的执行结果。
构造函数
cpp
// 构造一个包装了可调用对象 f 的 packaged_task。
template< class Callable > explicit packaged_task(Callable&& f)
cpp
#include <iostream>
#include <future>
#include <thread>
#include <functional>
// 异步任务函数
int asyncTask(int x) {
return x * x;
}
int main() {
// 创建一个包装了异步任务函数的 std::packaged_task
std::packaged_task<int(int)> task(asyncTask);
// 获取与任务关联的 std::future 对象
std::future<int> result = task.get_future();
// 在一个新线程中执行任务
std::thread t(std::move(task), 5);
// 在主线程中获取任务结果
std::cout << "Result from asyncTask: " << result.get() << std::endl;
// 等待线程完成
t.join();
return 0;
}
std::future get_future()
获取与 packaged_task 共享状态相关联的 std::future 对象。
3.promise
std::promise 是 C++11 标准库中的一个类模板,用于在多线程编程中创建异步任务和传递任务结果 。std::promise 可以与 std::future 配合使用,提供一种机制来在线程之间传递值或异常
。
std::promise 提供了一种机制,用于在一个线程中设置异步操作的结果,并允许另一个线程通过与 std::promise 关联的 std::future 对象获取结果
。std::promise 主要用于以下场景:
- 1.在一个线程中设置结果。
- 2.在另一个线程中等待结果并获取。
成员函数 | 作用 |
---|---|
std::future<T> get_future() | 获取与 std::promise 共享状态相关联的 std::future 对象 |
void set_value(const T& value) | 设置共享状态的结果值 |
void set_value(T&& value) | 设置共享状态的结果值,使用右值引用 |
void set_exception(std::exception_ptr p) | 设置共享状态的异常 |
以下是一个简单的示例,如何使用 std::promise 和 std::future 在两个线程之间传递结果:
cpp
#include <iostream>
#include <thread>
#include <future>
// 异步任务函数
void asyncTask(std::promise<int> promise) {
// 模拟一些工作
std::this_thread::sleep_for(std::chrono::seconds(2));
int result = 42; // 假设计算结果是42
// 设置结果
promise.set_value(result);
}
int main() {
// 创建一个 std::promise 对象
std::promise<int> promise;
// 获取与 promise 关联的 std::future 对象
std::future<int> result = promise.get_future();
// 启动一个线程执行异步任务
std::thread t(asyncTask, std::move(promise));
// 在主线程中等待并获取结果
std::cout << "Waiting for result..." << std::endl;
int value = result.get();
std::cout << "Result: " << value << std::endl;
// 等待线程完成
t.join();
return 0;
}
- std::future:表示异步操作的结果,可以阻塞等待结果完成。
- std::async:用于启动异步任务,返回一个 std::future 对象。
- std::promise:用于在线程之间设置和传递异步操作的结果,与 std::future 配合使用。
- std::packaged_task:用于包装可调用对象,使其异步执行,并通过 std::future 获取结果。
结束语
感谢阅读吾之文章,今已至此次旅程之终站 🛬。
吾望斯文献能供尔以宝贵之信息与知识也 🎉。
学习者之途,若藏于天际之星辰🍥,吾等皆当努力熠熠生辉,持续前行。
然而,如若斯文献有益于尔,何不以三连为礼?点赞、留言、收藏 - 此等皆以证尔对作者之支持与鼓励也 💞。