c++并发编程(异步)

1.promise和future

我们定义两个函数,分别为mythread,和mythread2:

先看mythread就行,第一个参数是primise用来储存返回值,第二个参数是整型值

再看主函数,启动了mythread,注意第二个参数promise对象不允许复制,必须是指针或者引用(引用传递不会尝试复制对象,只是传递对象的引用(或地址),因此引用传递不会触发复制操作),而且我们也希望把结果储存到这个对象本身而不是复制的副本中。

然后我们通过get_future获得返回值,要注意的future对象也不能被复制,而且也不能用引用传值(如果 std::future 作为引用传递,可能会引发设计上的问题和悬空引用,因为 future 对象是用来获取异步操作的结果的。它的设计是为了确保每个结果只能被获取一次,它可能在传递后可能已经被移动或变得无效),一般通过move(移动语义)来传递。它只能get一次,因为它会移动或消耗掉 future 对象,我们在mythread2中get了,所以main函数的get必须注释掉,否则就出现问题。

调用get_future时,不会阻塞等待,当调用wait或者get的时候,才会阻塞等待,直到设置了结果。

2.packaged_task的使用

packaged_task 是 C++11 引入的一种工具,用于将一个可调用对象(如函数、lambda 表达式、函数对象等)封装在一个任务中,并与 std::future 关联。它允许异步执行任务并在任务完成时获取结果或异常。

packaged_task的间接调用

#include <chrono>
#include <future>
#include <iostream>
#include <thread>

using namespace std;

int mythread(int mypar) {
  cout << mypar << endl;
  cout << "mythread() Start!"
       << "threadid=" << this_thread::get_id() << endl;
  const chrono::milliseconds dura(2000);
  this_thread::sleep_for(dura);

  return 5;
}

int main() {
  cout << "main threadid=" << this_thread::get_id() << endl;

  packaged_task<int(int)> mypt(mythread);
  future<int> result = mypt.get_future(); // 获取future对象

  // 使用std::move传递任务对象
  thread t1(move(mypt), 1); // 1代表线程函数的参数
  t1.join();                // 等待线程完成

  // 线程完成后再获取结果
  cout << result.get() << endl; // 只调用一次get

  return 0;
}

packaged_task的直接调用

直接调用,整个任务都在主线程执行,没有创建新线程。

3.可多次get的shared_future

其实就是调用future::share方法,shared_future允许多次get

int mythread(int mypar) {
  cout << mypar << endl;
  cout << "mythread() Start! threadid=" << this_thread::get_id() << endl;
  chrono::milliseconds dura(2000); // 创建一个持续2秒的时间间隔
  this_thread::sleep_for(dura);    // 让线程休眠2秒
  return 5;                        // 返回结果5
}

// 函数mythread2接受一个shared_future<int>对象
// 这个函数从shared_future中获取结果并打印
void mythread2(shared_future<int> tmpf) {
  auto result = tmpf.get(); // 调用get()获取结果,shared_future允许多次调用get()
  cout << "mythread2 result: " << result << endl;
}

int main() {
  cout << "main threadid=" << this_thread::get_id() << endl;

  // 创建一个packaged_task对象,任务为mythread函数
  packaged_task<int(int)> mypt(mythread);
  // 获取与packaged_task关联的future对象
  future<int> fu1 = mypt.get_future();

  // 使用std::move将mypt传递给线程t1
  // 因为packaged_task不能被复制,只能被移动
  thread t1(move(mypt), 1); // 启动线程t1,传递参数1
  t1.join();                // 等待线程t1完成

  // 创建shared_future对象,它可以被多个线程共享
  shared_future<int> fu2 = fu1.share();

  // 检查fu1的有效性
  // 在share()之后,fu1将变得无效
  bool iscanget = fu1.valid();
  cout << "有效or无效?" << iscanget << endl;

  // 第一次调用fu2.get(),会阻塞直到任务完成并返回结果
  auto m1 = fu2.get();
  // 第二次调用fu2.get(),shared_future允许多次获取结果
  auto m2 = fu2.get();
  cout << "结果为:" << m1 << "  " << m2 << endl;
  // 启动线程t2,传递shared_future对象fu2
  // 这个线程会调用mythread2函数
  thread t2(mythread2, fu2);
  t2.join(); // 等待线程t2完成

  return 0;
}

4.async

(1)基本语法:

      std::future<return_type> result = std::async(std::launch::policy, function, args...);

  • std::launch::policy:指定异步操作的启动策略。可以是 std::launch::async(强制异步执行)或 std::launch::deferred(延迟执行,直到调用 get()wait())。
  • function:要异步执行的函数或函数对象。
  • args...:传递给函数的参数。

(2)基本特性:

  • 启动策略:

    • std::launch::async:强制异步执行任务,通常会在新线程中执行。
    • std::launch::deferred:延迟执行任务,直到调用 get()wait()。在调用 get() 时,如果任务还没有开始,deferred 会在当前线程中同步执行任务。
    • 可以不指定启动策略,默认为两者的组合,具体行为取决于实现。
  • 异常处理:

    • 如果异步任务抛出异常,可以通过 get() 访问该异常,get() 会重新抛出任务中的异常。
  • 结果获取:

    • 使用 get() 方法获取任务的结果。get() 会阻塞直到任务完成并返回结果。
    • 使用 wait() 方法可以等待任务完成,但不获取结果。
  • 自动管理:

    • std::async 会自动处理任务的生命周期和线程的创建/销毁,无需显式管理线程的启动和结束。
  • 避免资源泄漏:

    • 使用 std::future 对象可以避免手动管理线程资源,有效减少资源泄漏的风险。

(3)示例代码1

在这个示例中,我们启动了三个并行的异步任务,每个任务计算一个范围的整数之和,并使用 get() 方法获取结果并打印。



int calculateSum(int start, int end)
{
    return accumulate(start, end, 0);
}

int main()
{
    vector<future<int>> futures;
    
    // 启动多个异步任务
    futures.push_back(async(launch::async, calculateSum, 1, 100));
    futures.push_back(async(launch::async, calculateSum, 100, 200));
    futures.push_back(async(launch::async, calculateSum, 200, 300));

    // 获取结果并输出
    for (auto& f : futures)
    {
        cout << "Sum: " << f.get() << endl;
    }

    return 0;
}

(4)示例代码2

 定义一个简单的线程函数
int mythread() {
  cout << "mythread() Start! threadid=" << this_thread::get_id() << endl;
  chrono::milliseconds dura(3000); // 创建一个3000毫秒(3秒)的时间间隔
  this_thread::sleep_for(dura); // 让当前线程休眠3秒
  return 5;                     // 返回结果5
}

int main() {
  cout << "main threadid=" << this_thread::get_id() << endl;

  // 使用 std::async 启动异步任务,使用 std::launch::deferred 启动策略
  future<int> result = async(launch::deferred, mythread);

  // 由于使用了 launch::deferred,mythread() 不会立即执行,而是延迟到调用 get()
  // 或 wait() 时才执行
  // 调用 result.get(),这会阻塞直到 mythread() 执行完并返回结果
  int def = result.get(); // 获取任务的返回值,mythread() 会在这里执行
  cout << def << endl; // 打印返回值

  // 也可以调用 result.wait() 来等待任务完成,但不会获取结果
  // result.wait(); // 阻塞直到任务完成,但不会返回结果

  return 0;
}

(5)示例代码3

// 定义一个简单的线程函数
int mythread() {
  cout << "mythread() Start! threadid=" << this_thread::get_id() << endl;
  chrono::milliseconds dura(5000); // 创建一个5000毫秒(5秒)的时间间隔
  this_thread::sleep_for(dura); // 让当前线程休眠5秒
  return 5;                     // 返回结果5
}

int main() {
  cout << "main threadid=" << this_thread::get_id() << endl;

  // 使用 std::async 启动异步任务,使用默认策略
  future<int> result = async(mythread);

  // 等待最多4秒检查异步任务的状态
  future_status status = result.wait_for(chrono::seconds(4));

  if (status == future_status::timeout) {
    cout << "线程还没执行完!" << endl;
    // 在这里等待异步任务完成,并获取结果
    cout << result.get();
  } else if (status == future_status::ready) {
    cout << "线程成功执行完毕!" << endl;
    // 异步任务已经完成,可以获取结果
    cout << result.get();
  } else if (status == future_status::deferred) {
    cout << "线程被延迟执行!" << endl;
    // 线程被延迟执行,调用 get() 会立即执行任务
    cout << result.get() << endl;
  }
  return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值