async、future、shared_future、promise、packaged_task练习题目

练习题 1: 使用 std::futurestd::async

编写一个程序,使用 std::async 启动一个异步任务,该任务计算两个整数的和。主线程等待任务完成并输出结果。

题目要求:
  • 使用 std::async 创建异步任务。

  • 主线程获取任务的结果并打印。

#include<iostream>
#include<future>
#include<thread>
using namespace std;

int compute_sum(int x, int y) {
    cout << "子线程开始计算.." << endl;
    return x + y;
}

int main() {
    cout << "主线程开始..." << endl;
    int x = 1, y = 2;
    future<int> fut = async(launch::async, compute_sum, x, y);

    int result = fut.get();
    cout << "主线程得到答案:" << result << endl;
    return 0;
}

练习题 2: 使用 std::shared_future

编写一个程序,创建一个异步任务,计算一个整数的平方,并使用 std::shared_future 在多个线程中共享计算结果。

题目要求:
  • 使用 std::async 启动一个计算平方的任务。

  • 使用 std::shared_future 在多个线程中共享该结果。

  • 每个线程打印结果。

#include<iostream>
#include<future>
#include<thread>
#include<vector>
#include<mutex>
using namespace std;

mutex mtx;

int compute_sqr(int x) {
    lock_guard<mutex> lock(mtx);
    cout << "现在正在计算的线程是:" << this_thread::get_id() << endl;
    return x * x;
}

int main() {
    cout << "主线程开始..." << endl;
    future<int> fut = async(launch::async, compute_sqr, 5);

    shared_future<int> shared_fut = fut.share();

    vector<thread> threads;
    for (int i = 0; i < 3; i++) {
        threads.push_back(thread([&shared_fut] {
            lock_guard<mutex> lock(mtx);
            cout << "现在运行的线程是:" << this_thread::get_id() << " 获得sqr=" << shared_fut.get() << endl;
            }));
    }

    for (auto& t : threads) {
        t.join();
    }

    cout << "主线程结束" << endl;
    return 0;
}

这里对于shared_future的理解更加深刻了,async线程只会运行一次,shared_future使得结果可以让不同线程多次获取


练习题 3: 使用 std::packaged_taskstd::thread

编写一个程序,使用 std::packaged_task 包装一个函数,该函数计算给定整数的阶乘。使用 std::thread 来执行任务,并获取结果。

题目要求:
  • 使用 std::packaged_task 包装一个计算阶乘的函数。

  • 使用 std::thread 来执行这些任务。

  • 获取并打印结果。

使用ref,引用传递,需要注意的就是资源是否会释放问题,这里使用的get,join都是阻塞,所以不会有问题

#include<iostream>
#include<future>
#include<thread>
using namespace std;

mutex mtx;

int compute_factor(int x) {
    int res = 1;
    for (int i = 1; i <= x; i++)res *= i;
    return res;
}

int main() {
    cout << "主线程开始..." << endl;
    packaged_task<int(int)> task(compute_factor);

    int x = 5;
    thread t(ref(task), x);
    future<int> fut = task.get_future();

    cout << x << "的阶乘=" << fut.get() << endl;

    t.join();

    cout << "主线程结束" << endl;
    return 0;
}

使用move,要在thread移动前获取task.get_future,否则task资源已经被转移了,无法获取

#include<iostream>
#include<future>
#include<thread>
using namespace std;

int compute_factor(int x) {
    int result = 1;
    for (int i = 1; i <= x; ++i) {
        result *= i;
    }
    return result;
}

int main() {
    cout << "主线程开始..." << endl;
    packaged_task<int(int)> task(compute_factor);
    future<int> fut = task.get_future();

    int x = 5;
    thread t(move(task), x);

    cout << x << "的阶乘=" << fut.get() << endl;
    t.join();

    cout << "主线程结束" << endl;
    return 0;
}

练习题 4: 使用 std::promisestd::future

编写一个程序,使用 std::promisestd::future 在两个线程之间传递数据。一个线程将结果设置到 promise 中,另一个线程获取结果。

题目要求:
  • 创建一个 std::promise 对象。

  • 在一个线程中使用 set_value 设置结果。

  • 在另一个线程中使用 future.get() 获取并打印结果。

引用版本

#include<iostream>
#include<future>
#include<thread>
#include<vector>
using namespace std;


int compute_add(promise<int>& pro,int x,int y) {
    int res = x + y;
    pro.set_value(res);
    cout << "和线程" << this_thread::get_id() << " 计算结果=" << res << endl;
    return res;
}

int compute_sqr(int x) {
    int res = x * x;
    cout << "平方线程" << this_thread::get_id() << " 计算结果=" << res << endl;
    return res;
}

int main() {
    cout << "主线程开始..." << endl;
    
    promise<int> pro;
    thread t1(compute_add, ref(pro), 3, 4);

    future<int> fut = pro.get_future();

    thread t2(compute_sqr, fut.get());

    t1.join();
    t2.join();
    cout << "主线程结束" << endl;
    return 0;
}

移动版本

  • promise<int>&& pro 的移动:通过 move(pro),将 promise 移动到 t1 线程中。这确保了在加法线程计算后,promise 的值会被正确设置。

  • future 传递到线程中compute_sqr 函数现在接收 future<int>& fut,并在里面调用 fut.get() 获取加法结果。这样就不需要在主线程中调用 fut.get(),避免了重复调用 get() 的问题。

#include<iostream>
#include<future>
#include<thread>
#include<vector>
using namespace std;

int compute_add(promise<int>&& pro, int x, int y) {
    int res = x + y;
    pro.set_value(res);
    cout << "和线程 " << this_thread::get_id() << " 计算结果=" << res << endl;
    return res;
}

int compute_sqr(future<int>& fut) {
    int res = fut.get(); // 获取加法结果并计算平方
    cout << "平方线程 " << this_thread::get_id() << " 计算结果=" << res * res << endl;
    return res * res;
}

int main() {
    cout << "主线程开始..." << endl;

    promise<int> pro;
    future<int> fut = pro.get_future(); // 获取与 promise 相关的 future

    thread t1(compute_add, move(pro), 3, 4); // 将 promise 移动到线程中
    thread t2(compute_sqr, ref(fut)); // 将 future 传递给平方线程

    t1.join();
    t2.join();

    cout << "主线程结束" << endl;
    return 0;
}


练习题 5: 任务队列与 std::packaged_task

创建一个任务队列,队列中的每个任务计算不同的整数的平方。使用 std::packaged_task 将每个计算包装成任务,并使用线程池执行这些任务。最终获取并输出所有任务的结果。

题目要求:
  • 使用 std::packaged_task 创建多个任务,每个任务计算不同整数的平方。

  • 将任务存储在容器中,并使用线程池来执行。

  • 最终收集并输出每个任务的结果。

#include<iostream>
#include<future>
#include<thread>
#include<vector>
using namespace std;



int compute_sqr(int x) {
    int res = x * x;
    return res;
}

int main() {
    cout << "主线程开始..." << endl;
    vector<packaged_task<int(int)>> tasks;
    vector<future<int>> futures;


    for (int i = 0; i < 10; i++) {
        tasks.push_back(packaged_task(compute_sqr));
        futures.push_back(tasks[i].get_future());
    }

    vector<thread> threads;
    for (int i = 0; i < 10; i++) {
        threads.push_back(thread(move(tasks[i]),i+1));
    }

    for (auto& fut : futures) {
        cout << fut.get() << endl;
    }

    for (auto& t : threads) {
        t.join();
    }



    cout << "主线程结束" << endl;
    return 0;
}


练习题 6: 使用 std::future 和超时

编写一个程序,使用 std::future 启动一个异步任务,计算一个大数的平方,主线程在 1 秒钟后检查任务是否完成。如果任务未完成,则打印 “任务超时”。

题目要求:
  • 启动一个计算大数平方的异步任务。

  • 使用 std::future::wait_for 设置超时,如果任务未在超时时间内完成,则打印 “任务超时”。

#include<iostream>
#include<future>
#include<thread>
#include<vector>
using namespace std;



int compute_sqr(int x) {
    this_thread::sleep_for(2s);
    int res = x * x;
    return res;
}

int main() {
    cout << "主线程开始..." << endl;
    
    future<int> fut = async(launch::async, compute_sqr, 5);

    future_status fut_status = fut.wait_for(1s);

    if (fut_status == future_status::timeout) {
        cout << "任务超时" << endl;
    }
    else {
        cout << "任务完成,结果=" << fut.get() << endl;
    }

    cout << "主线程结束" << endl;
    return 0;
}


练习题 7: 使用 std::future 和异常处理

编写一个程序,使用 std::async 启动一个异步任务,该任务计算一个数字的倒数。如果输入为零,则抛出异常并在主线程中捕获该异常。

题目要求:
  • 使用 std::async 启动异步任务计算倒数。

  • 如果输入为零,则抛出异常。

  • 在主线程中捕获并打印异常信息。

#include<iostream>
#include<future>
#include<thread>
#include<vector>
using namespace std;



double compute_reciprocal(double x) {
    if (x == 0)throw invalid_argument("倒数不能为0!");
    return 1.0 / x;
}

int main() {
    cout << "主线程开始..." << endl;
    
    double x = 0;
    future<double> fut = async(launch::async, compute_reciprocal, x);

    try {
        cout << x << "的倒数是" << fut.get() << endl;
    }catch(const invalid_argument &e){
        cout << e.what() << endl;
    }

    cout << "主线程结束" << endl;
    return 0;
}


练习题 8: 使用 std::shared_future 和多个线程

编写一个程序,启动一个异步任务,计算 100 到 200 之间所有数字的和。多个线程使用 std::shared_future 获取该任务的结果并输出结果。

题目要求:
  • 使用 std::async 启动异步任务,计算和。

  • 使用 std::shared_future 将结果共享给多个线程。

  • 每个线程输出结果。

#include<iostream>
#include<future>
#include<thread>
#include<vector>
using namespace std;



int compute_sum() {
    int res = 0;
    for (int i = 100; i <= 200; i++)res += i;
    return res;
}

int main() {
    cout << "主线程开始..." << endl;
    
    future<int> fut = async(launch::async, compute_sum);
    shared_future<int> shared_fut = fut.share();

    vector<thread> threads;
    for (int i = 0; i < 5; i++) {
        threads.push_back(thread([&shared_fut] {cout << "线程" << this_thread::get_id() << "拿到共享结果为=" << shared_fut.get() << endl; }));
    }

    for (auto& t : threads) {
        t.join();
    }

    cout << "主线程结束" << endl;
    return 0;
}


练习题 9: 使用 std::packaged_task 和多线程计算任务

编写一个程序,创建一个 std::packaged_task 对象,包装一个计算两个整数最大公约数(GCD)的函数。然后使用多个线程分别计算不同整数对的 GCD,获取结果并输出。

题目要求:
  • 使用 std::packaged_task 包装计算 GCD 的任务。

  • 使用多个线程计算不同整数对的 GCD。

  • 输出每对整数的 GCD 结果。

#include<iostream>
#include<future>
#include<thread>
#include<vector>
using namespace std;

int compute_gcd(int a, int b) {
    return b == 0 ? a : compute_gcd(b, a % b);
}

int main() {
    cout << "主线程开始..." << endl;

    vector<thread> threads;
    vector<future<int>> futures;

    // 直接在循环中创建任务并传递给线程
    for (int i = 0; i < 5; i++) {
        packaged_task<int(int, int)> task(compute_gcd);
        futures.push_back(task.get_future());
        threads.push_back(thread(move(task), i * 10, i * 5));
    }

    // 等待所有线程并获取结果
    for (auto& fut : futures) {
        cout << "公约数为=" << fut.get() << endl;
    }

    // 等待所有线程完成
    for (auto& t : threads) {
        t.join();
    }

    cout << "主线程结束" << endl;
    return 0;
}



练习题 10: 使用 std::promise 实现任务通知

编写一个程序,创建一个 std::promise 对象,主线程通过 promise 设置一个值,另一个线程等待该值并输出通知。

题目要求:
  • 使用 std::promise 设置一个整数值。

  • 主线程通过 promise 设置值,另一个线程获取并输出该值。

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
using namespace std;

void task(promise<int>& prom) {
    // 模拟一个耗时操作
    this_thread::sleep_for(2s);
    prom.set_value(100); // 设置任务完成的结果
}

int main() {
    cout << "主线程开始..." << endl;

    promise<int> prom;
    future<int> fut = prom.get_future();

    thread t(task, ref(prom)); // 启动一个线程,模拟任务执行

    // 主线程等待任务完成并获取结果
    cout << "主线程等待任务完成..." << endl;
    cout << "Received value: " << fut.get() << endl; // 获取并打印结果

    t.join(); // 等待子线程完成
    cout << "主线程结束" << endl;
    return 0;
}


### shared_future 的基本概念 `std::shared_future` 是 C++11 引入的一种机制,用于处理异步操作的结果。与 `std::future` 不同的是,`std::shared_future` 允许在多个线程中多次获取结果。这意味着它可以被用来在多个线程之间共享异步操作的结果,而不会导致状态无效的问题[^2]。 ### shared_future 的构造函数 `std::shared_future` 提供了多个构造函数,包括但不限于: - 默认构造函数 `shared_future()`,创建一个不持有任何异步状态的 `shared_future` 实例。 - 拷贝构造函数 `shared_future(const shared_future& other)`,从另一个 `shared_future` 实例复制异步状态。 - 移动构造函数 `shared_future(shared_future&& other)`,从另一个 `shared_future` 实例移动异步状态。 - 从 `std::future` 移动构造 `shared_future(std::future<T>&& other)`,将 `std::future` 的异步状态转移到 `shared_future` 中[^1]。 ### shared_future 的使用示例 下面是一个简单的 `std::shared_future` 使用示例,展示了如何在一个异步操作完成后,在多个线程中获取该操作的结果: ```cpp #include <iostream> #include <future> #include <thread> void print_result(std::shared_future<int> result) { std::cout << "Result is " << result.get() << std::endl; } int main() { std::promise<int> p; std::shared_future<int> f = p.get_future(); std::thread t1(print_result, f); std::thread t2(print_result, f); // 设置值 p.set_value(42); // 等待线程完成 t1.join(); t2.join(); return 0; } ``` 在这个例子中,`std::promise` 创建了一个异步操作,并通过 `get_future` 方法获取了一个 `std::shared_future`。然后,这个 `std::shared_future` 被传递给了两个线程,这两个线程都调用了 `get` 方法来获取结果。由于使用了 `std::shared_future`,即使在两个线程中调用 `get` 方法,也不会导致状态无效的问题[^2]。 ### shared_future 的注意事项 - 当从 `std::future` 创建 `std::shared_future` 时,原始的 `std::future` 将失去对异步状态的所有权,不能再通过 `std::future` 获取结果。 - `std::shared_future` 的 `get` 方法可以被多次调用,每次都会返回相同的结果或异常。 - 在多线程环境中使用 `std::shared_future` 时,确保所有线程都完成了对 `get` 方法的调用之后再销毁 `std::shared_future` 对象,以避免悬挂指针或引用的问题[^3]。 ### shared_futurepromise 的关系 `std::promise` 和 `std::future` 之间的关系类似于生产者和消费者的关系。`std::promise` 用于设置值,而 `std::future` 用于获取这些值。当需要在多个线程中共享异步操作的结果时,可以通过 `std::future` 的 `share` 方法获得一个 `std::shared_future`,从而允许多个线程安全地访问同一个异步结果[^4]。 ### shared_future 的线程安全性 `std::shared_future` 的设计考虑了线程安全性。当多个线程同时调用 `get` 方法时,它们能够安全地获取异步操作的结果,而不会引发数据竞争或其他并发问题。这是因为 `std::shared_future` 内部的实现确保了对共享状态的访问是线程安全的[^2]。 ### shared_future 的性能考量 虽然 `std::shared_future` 提供了方便的多线程间共享异步结果的能力,但在某些情况下可能需要考虑其性能影响。由于 `std::shared_future` 需要维护额外的状态信息以支持多线程访问,因此相比 `std::future` 可能会有轻微的性能开销。在性能敏感的应用中,应仔细评估是否确实需要使用 `std::shared_future` 并考虑其他可能的优化策略。 ### shared_future 的高级用法 除了基本的异步结果共享外,`std::shared_future` 还可以与其他 C++ 标准库特性结合使用,如 `std::async` 和 `std::packaged_task`,以实现更复杂的并发模式。例如,可以使用 `std::async` 创建异步任务,并通过 `std::shared_future` 在多个线程中等待任务完成并获取结果。这种模式非常适合需要并行处理多个任务并最终汇总结果的场景[^2]。 ### shared_future 的异常处理 `std::shared_future` 支持异常处理,这意味着如果异步操作抛出了异常,那么这个异常可以通过 `get` 方法捕获。当调用 `get` 方法时,如果异步操作已经抛出了异常,那么 `get` 方法将重新抛出该异常。这对于在多个线程中统一处理错误情况非常有用[^2]。 ### shared_future 的生命周期管理 正确管理 `std::shared_future` 的生命周期对于确保程序的稳定性和正确性至关重要。当不再需要访问异步结果时,应及时释放 `std::shared_future` 对象,以便系统可以回收相关资源。特别是在使用 `std::shared_ptr` 管理 `std::shared_future` 的情况下,需要注意避免循环引用导致的内存泄漏[^2]。 ### shared_future 的调试技巧 调试涉及 `std::shared_future` 的并发程序时,可以采用以下几种技巧: - 使用日志记录每个线程调用 `get` 方法的时间点,帮助理解异步操作的实际执行顺序。 - 利用调试器的条件断点功能,在特定条件下暂停程序执行,以便检查异步状态。 - 对于复杂的并发逻辑,考虑编写单元测试来验证 `std::shared_future` 的行为是否符合预期,尤其是在异常处理和资源释放方面[^2]。 ### shared_future 的最佳实践 为了有效地使用 `std::shared_future`,建议遵循以下最佳实践: - 在需要多线程共享异步结果的情况下优先选择 `std::shared_future`。 - 尽量减少对 `get` 方法的调用次数,特别是在性能关键路径上。 - 当不再需要访问异步结果时,及时释放 `std::shared_future` 对象,以释放占用的资源。 - 在设计并发程序时,充分考虑异常处理和资源管理,确保程序的健壮性和可靠性。 ### shared_future 的替代方案 尽管 `std::shared_future` 是 C++ 标准库中处理异步操作的强大工具,但在某些特定场景下,可能存在更适合的选择。例如,对于需要高度定制化异步行为的应用,可以考虑使用第三方库提供的异步框架,这些框架往往提供了更加丰富和灵活的功能。此外,对于简单的同步需求,直接使用 `std::mutex` 和 `std::condition_variable` 可能更为直观和高效。 ### shared_future 的局限性 尽管 `std::shared_future` 提供了许多有用的功能,但也存在一些局限性: - `std::shared_future` 的实现依赖于具体的编译器和标准库实现,不同平台上的行为可能会有所不同。 - 对于非常简单的应用场景,使用 `std::shared_future` 可能显得过于重量级。 - 在某些情况下,`std::shared_future` 的性能可能不如手动管理的同步机制[^2]。 ### shared_future 的未来展望 随着 C++ 标准的不断发展,`std::shared_future` 的功能和性能也在持续改进。未来的 C++ 标准可能会引入新的特性,进一步增强 `std::shared_future` 的灵活性和效率。开发者应密切关注 C++ 标准的发展动态,以便及时利用最新的语言特性和优化成果。 ### shared_future 的常见问题解答 #### Q: 如何从 `std::future` 创建 `std::shared_future`? A: 可以通过调用 `std::future` 的 `share` 方法来创建 `std::shared_future`。这将转移 `std::future` 的所有权,使其不能再用于获取结果。 #### Q: `std::shared_future` 的 `get` 方法会多次计算任务吗? A: 不会。`std::shared_future` 的 `get` 方法只会计算一次任务,之后的所有调用都将返回相同的结果或异常。 #### Q: 如何确保 `std::shared_future` 在多线程环境中的正确使用? A: 确保所有线程都完成了对 `get` 方法的调用之后再销毁 `std::shared_future` 对象,以避免悬挂指针或引用的问题。 #### Q: `std::shared_future` 是否支持异常处理? A: 是的,`std::shared_future` 支持异常处理。如果异步操作抛出了异常,那么这个异常可以通过 `get` 方法捕获[^2]。 #### Q: `std::shared_future` 的生命周期应该如何管理? A: 当不再需要访问异步结果时,应及时释放 `std::shared_future` 对象,以便系统可以回收相关资源。特别是在使用 `std::shared_ptr` 管理 `std::shared_future` 的情况下,需要注意避免循环引用导致的内存泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值