首页 > 后端开发 > C++ > 正文

C++中如何使用结构化并发_并发编程新模式

裘德小鎮的故事
发布: 2025-07-17 12:06:03
原创
466人浏览过

结构化并发能有效解决传统并发模型中的复杂问题。1.它通过确保并发任务的生命周期与代码结构对应,使代码更易理解和维护。2.c++++虽无原生支持,但可借助std::future、std::async及自定义线程池模拟实现。3.死锁预防包括资源排序、超时机制、避免持锁执行耗时操作、使用lock_guard或unique_lock管理锁以及采用无锁数据结构。4.c++20协程简化了异步代码编写,提升了错误处理和调试体验,并能与结构化并发结合使用。5.除标准库外,boost.asio适用于异步i/o,intel tbb适合并行算法开发,hpx则用于高性能计算场景。选择合适工具取决于具体项目需求,关键在于理解各方案的优势与适用范围。

C++中如何使用结构化并发_并发编程新模式

并发编程的未来,也许就藏在结构化并发里。它试图解决传统并发模型中那些令人头疼的问题,让代码更易于理解、调试和维护。C++虽然没有原生支持,但我们可以通过一些库和技巧来实现这种模式。

C++中如何使用结构化并发_并发编程新模式

解决方案

C++中如何使用结构化并发_并发编程新模式

要在 C++ 中实现结构化并发,关键在于确保并发任务的生命周期与代码的结构相对应。这意味着父任务应该能够追踪并管理其子任务,并在子任务完成后才继续执行。这避免了常见的并发问题,比如子任务泄漏或父任务过早结束。

立即学习C++免费学习笔记(深入)”;

C++中如何使用结构化并发_并发编程新模式

一种常见的方法是使用 std::futurestd::async,结合自定义的线程池或任务调度器。std::future 允许你获取异步操作的结果,而 std::async 则提供了一种启动异步任务的便捷方式。

#include <iostream>
#include <future>
#include <vector>
#include <algorithm>

// 假设的线程池(简化版)
class ThreadPool {
public:
    ThreadPool(size_t num_threads) : threads(num_threads) {
        for (size_t i = 0; i < num_threads; ++i) {
            threads[i] = std::thread([this] {
                while (true) {
                    std::function<void()> task;

                    {
                        std::unique_lock<std::mutex> lock(queue_mutex);
                        condition.wait(lock, [this] { return stop || !tasks.empty(); });
                        if (stop && tasks.empty())
                            return;
                        task = std::move(tasks.front());
                        tasks.pop();
                    }

                    task();
                }
            });
        }
    }

    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::invoke_result<F, Args...>::type> {
        using return_type = typename std::invoke_result<F, Args...>::type;
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );

        std::future<return_type> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            if (stop)
                throw std::runtime_error("enqueue on stopped ThreadPool");

            tasks.emplace([task]() { (*task)(); });
        }
        condition.notify_one();
        return res;
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread &thread : threads)
            thread.join();
    }

private:
    std::vector<std::thread> threads;
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop = false;
};


int main() {
    ThreadPool pool(4);

    std::vector<std::future<int>> results;
    for (int i = 0; i < 8; ++i) {
        results.emplace_back(
            pool.enqueue([i]() {
                std::cout << "Task " << i << " running on thread " << std::this_thread::get_id() << std::endl;
                std::this_thread::sleep_for(std::chrono::milliseconds(100 * (i+1))); // 模拟不同任务耗时
                return i * 2;
            })
        );
    }

    // 等待所有任务完成
    for (auto &result : results) {
        std::cout << "Result: " << result.get() << std::endl;
    }

    std::cout << "All tasks completed." << std::endl;

    return 0;
}
登录后复制

这个例子展示了如何使用线程池来管理并发任务,并使用 std::future 来等待任务完成。虽然它不是一个完整的结构化并发实现,但它演示了关键的思想:控制并发任务的生命周期,并确保父任务等待子任务完成。

如何避免C++并发编程中的死锁?

死锁是并发编程中的一个常见问题,通常发生在多个线程互相等待对方释放资源时。避免死锁的关键在于理解死锁的成因,并采取相应的预防措施。

  • 资源排序: 如果所有线程都按照相同的顺序获取资源,就可以避免循环等待。例如,如果线程 A 需要同时持有锁 L1 和 L2,而线程 B 也需要同时持有这两个锁,那么确保所有线程都先获取 L1,再获取 L2,就可以避免死锁。

  • 超时机制: 在尝试获取锁时,设置一个超时时间。如果超过了超时时间仍然无法获取锁,就释放已经持有的锁,稍后再尝试。这可以打破循环等待的局面。

  • 避免持有锁时执行耗时操作: 尽量减少持有锁的时间,避免在持有锁时执行耗时的操作,例如 I/O 操作或复杂的计算。这可以降低其他线程等待锁的概率。

  • 使用 std::lock_guardstd::unique_lock 这两个类可以帮助你自动管理锁的生命周期,确保在离开作用域时自动释放锁。std::unique_lock 提供了更灵活的锁管理方式,例如可以延迟获取锁或尝试获取锁。

  • 使用无锁数据结构: 在某些情况下,可以使用无锁数据结构来避免锁的使用。无锁数据结构使用原子操作来实现并发访问,可以避免死锁的发生。

    豆包AI编程
    豆包AI编程

    豆包推出的AI编程助手

    豆包AI编程 483
    查看详情 豆包AI编程

C++20 协程对结构化并发有什么影响?

C++20 引入了协程,这为实现结构化并发提供了新的可能性。协程允许你编写看起来像同步代码的异步代码,从而简化了并发编程。

  • 简化异步代码: 协程可以让你以更自然的方式编写异步代码,避免了回调地狱或复杂的 future 链。

  • 更好的错误处理: 协程可以更容易地处理异步操作中的错误。你可以使用 try-catch 块来捕获协程中的异常,而无需使用复杂的错误处理机制。

  • 更容易的调试: 协程可以更容易地调试,因为它们的代码看起来更像同步代码。你可以使用调试器来单步执行协程,并查看其状态。

  • 与结构化并发的结合: 可以将协程与结构化并发的思想结合起来,创建一个更易于理解和维护的并发模型。例如,你可以使用协程来启动和管理子任务,并确保父任务等待子任务完成。

虽然 C++20 协程本身并不直接提供结构化并发,但它们提供了一种强大的工具,可以用来构建结构化并发系统。

除了std::future和线程池,还有哪些C++库或技术可以用于实现结构化并发?

除了 std::future 和线程池,还有一些其他的 C++ 库和技术可以用于实现结构化并发:

  • Boost.Asio: Boost.Asio 是一个跨平台的 C++ 库,用于异步 I/O、定时器和并发编程。它提供了一套丰富的工具,可以用来构建高性能的并发应用程序。Asio 允许你定义异步操作,并将它们绑定到特定的执行上下文(例如线程池)。你可以使用 Asio 来实现结构化并发,通过管理异步操作的生命周期,并确保父任务等待子任务完成。

  • Intel TBB (Threading Building Blocks): Intel TBB 是一个 C++ 模板库,用于并行编程。它提供了一套高级的抽象,可以用来简化并行算法的开发。TBB 包含一些用于任务调度的组件,例如 task_groupparallel_invoke,可以用来实现结构化并发。task_group 允许你创建一组并发执行的任务,并等待它们全部完成。parallel_invoke 允许你并行执行多个函数,并等待它们全部完成。

  • HPX (High Performance ParalleX): HPX 是一个 C++ 运行时系统,用于高性能计算。它提供了一套分布式的任务调度机制,可以用来构建可扩展的并行应用程序。HPX 支持结构化并发,允许你创建嵌套的任务树,并管理任务的生命周期。

选择哪种库或技术取决于你的具体需求。如果你需要一个跨平台的异步 I/O 库,那么 Boost.Asio 是一个不错的选择。如果你需要一个用于并行算法的模板库,那么 Intel TBB 可能更适合你。如果你需要一个用于高性能计算的运行时系统,那么 HPX 可能是最佳选择。重要的是理解这些库和技术的优缺点,并选择最适合你的项目的工具。

以上就是C++中如何使用结构化并发_并发编程新模式的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号