C++开发基础之异步编程模式

1.前言

异步编程模式是现代C++开发中不可或缺的一部分。在处理IO密集型或者长时间运行的任务时,异步编程可以提高程序的响应性和吞吐量。异步编程模式通常适用于以下场景:

1. 需要执行耗时的 I/O 操作:例如读取文件、数据库操作、网络请求等,这些操作通常需要花费较长时间。如果使用同步方法,程序将会被阻塞,无法响应其他操作。而使用异步编程模式,可以在这些操作执行过程中继续处理其他任务,从而提高程序的并发性能和响应能力。

2. 需要执行计算密集型操作:虽然异步编程通常与 I/O 操作相关联,但它也可以用于执行计算密集型操作,例如图像处理、科学计算等。在这种情况下,异步编程可以帮助程序更好地利用多核处理器等硬件资源,加快计算速度。

3. 需要处理大量的并发请求:例如 Web 服务器、消息队列等,这些应用程序需要同时处理大量的请求,如果采用同步方式,程序将无法满足高并发的需求。而使用异步编程模式,可以充分利用系统资源,提高并发性能和可扩展性。

2.异步编程模式

  1. 回调函数(Callbacks):
    回调函数是异步编程最基本的形式之一。当一个操作完成后,它会调用一个预定义的回调函数来通知程序,以便继续执行其他操作。在C++中,回调函数可以使用函数指针、std::function或者Lambda表达式来定义。

  2. Future/Promise:
    Future/Promise是一种更高级别的异步编程模式,它允许程序等待异步操作完成并获取结果。在C++中,可以使用std::future和std::promise来实现Future/Promise模式。

  3. 协程(Coroutines):
    协程是一种轻量级的线程,它可以在同一线程内切换上下文并暂停/恢复执行。它可以用于实现高效的异步编程和状态机。在C++20中,协程已经成为了语言的一部分,并且可以使用co_await关键字来等待异步操作的完成。

  4. 异步/await(C++20):
    异步/await是一种与协程相关的模式,它允许程序以自然的顺序编写异步代码。在C++20中,可以使用co_await关键字来等待异步操作的完成,并使用co_return关键字来返回异步操作的结果。

  5. 线程池(Thread Pool):
    线程池是一种常见的并发模式,它可以提高程序的性能和资源利用率。一个线程池包含多个线程,在程序需要执行异步操作时,可以将任务提交到线程池中,以便在其中的一个线程上执行。在C++中,可以使用std::thread或者第三方库来实现线程池。

  6. Actor 模型:
    Actor模型是一种基于消息传递的并发模型,它允许多个独立的Actor同时运行,并通过消息进行通信。每个Actor都拥有自己的状态和行为,并且可以响应接收到的消息。在C++中,可以使用第三方库来实现Actor模型。

  7. Reactive Extensions:
    Reactive Extensions是一种基于事件流的编程模型,它允许程序以声明性的方式处理数据流。在C++中,可以使用rxcpplib或者其他第三方库来实现Reactive Extensions。

  8. 数据流处理框架:
    数据流处理框架是一种基于管道和过滤器的编程模型,它允许程序以可组合的方式处理数据流。在C++中,可以使用rxcpp或者其他第三方库来实现数据流处理框架。

3.各个模式特点

以下是C++开发中常见的异步编程模式,这些模式各有特点,可以根据具体需求选择适合的模式。

序号异步编程模式描述优势缺点适用场景学习曲线平台支持可读性性能
1回调函数(Callbacks)通过回调函数来处理异步操作完成后的结果和错误直接,易于理解可能导致回调地狱(callback hell),难以维护较简单的异步操作通用一般一般
2Future/Promise使用future对象获取异步操作的结果,promise对象设置结果直观,支持等待异步操作完成对于复杂的控制流较难处理单个异步操作的结果通用较好一般
3协程(Coroutines)使用协程来简化异步代码,避免回调地狱简化异步代码,易于编写和理解需要特定的编译器支持,不是标准的C++特性处理复杂的异步流程部分平台支持较好较好
4异步/await(C++20)使用co_await关键字来等待异步操作的完成简化异步代码,易于编写和理解C++20新特性,不是所有平台和编译器都支持新项目或有C++20支持的项目部分平台支持较好较好
5线程池(Thread Pool)使用线程池管理异步任务的执行,提高并发性能有效利用系统资源,降低线程创建和销毁开销需要合理调整线程池大小,可能出现任务处理不均衡高并发环境通用一般较好
6Actor 模型使用独立的“actor”对象处理消息和并发操作易于实现并发和消息传递,避免共享数据的竞争可能引入复杂性,需要谨慎处理消息顺序分布式和并发密集型应用程序通用一般一般
7Reactive Extensions使用可观察序列来处理异步事件和数据流强大的组合和转换操作,支持响应式编程范式学习曲线较陡,理解响应式编程概念较难响应式和事件驱动的应用程序部分平台支持较好较好
8数据流处理框架使用数据流处理框架(如RxCpp、Boost.Asio)来处理异步流数据提供丰富的数据处理操作符和工具学习成本较高,需要熟悉框架的特性复杂的数据流处理需求部分平台支持较好较好

4.各个示例程序

对每种异步编程模式的简要示例代码说明:

1. 回调函数(Callbacks)示例代码:

#include <iostream>
#include <functional>

void async_operation(std::function<void(int)> callback) {
    // 模拟异步操作
    int result = 42;
    callback(result);
}

int main() {
    async_operation([](int result) {
        std::cout << "异步操作完成,结果为: " << result << std::endl;
    });

    return 0;
}

输出结果

异步操作完成,结果为: 42

2. Future/Promise 示例代码:

在这里插入图片描述

#include <iostream>
#include <future>

int async_operation() {
    // 模拟异步操作
    return 42;
}

int main() {
    std::future<int> future_result = std::async(std::launch::async, async_operation);

    int result = future_result.get();
    std::cout << "异步操作完成,结果为: " << result << std::endl;

    return 0;
}

输出结果

异步操作完成,结果为: 42

3. 协程(Coroutines)示例代码:

#include <iostream>
#include <coroutine>

struct Task {
    struct promise_type {
        Task get_return_object() {
            return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
        }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_value(int value) { result = value; }
        void unhandled_exception() {}
        
        int result;
    };

    std::coroutine_handle<promise_type> coro;

    Task(std::coroutine_handle<promise_type> h): coro(h) {}
    ~Task() { if (coro) coro.destroy(); }
};

Task async_operation() {
    co_return 42;
}

int main() {
    Task task = async_operation();
    std::cout << "异步操作完成,结果为: " << task.coro.promise().result << std::endl;

    return 0;
}

输出结果

异步操作完成,结果为: 42

4. 异步/await(C++20)示例代码:

#include <iostream>
#include <future>

std::future<int> async_operation() {
    // 模拟异步操作
    return std::async(std::launch::async, []() {
        return 42;
    });
}

int main() {
    auto future_result = async_operation();
    int result = future_result.get();
    std::cout << "异步操作完成,结果为: " << result << std::endl;

    return 0;
}

输出结果

异步操作完成,结果为: 42

5. 线程池(Thread Pool)示例代码:

在这里插入图片描述

#include <iostream>
#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>

class ThreadPool {
public:
    ThreadPool(size_t num_threads) {
        for (size_t i = 0; i < num_threads; ++i) {
            threads.emplace_back([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>
    void enqueue(F&& f) {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            tasks.emplace(std::forward<F>(f));
        }
        condition.notify_one();
    }

    ~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);

    for (int i = 0; i < 8; ++i) {
        pool.enqueue([i] {
            std::cout << "任务 " << i << " 在线程池中执行" << std::endl;
        });
    }

    return 0;
}

输出结果

任务 0 在线程池中执行
任务 1 在线程池中执行
任务 2 在线程池中执行
任务 3 在线程池中执行
任务 4 在线程池中执行
任务 5 在线程池中执行
任务 6 在线程池中执行
任务 7 在线程池中执行

6. Actor 示例代码:

#include <iostream>
#include <thread>
#include <queue>
#include <mutex>

class Actor {
public:
    void sendMessage(int message) {
        std::lock_guard<std::mutex> lock(mutex);
        messages.push(message);
    }

    void processMessages() {
        while (!messages.empty()) {
            int message = messages.front();
            messages.pop();
            std::cout << "接收到消息: " << message << std::endl;
        }
    }

private:
    std::queue<int> messages;
    std::mutex mutex;
};

int main() {
    Actor actor;

    std::thread producer([&actor] {
        for (int i = 0; i < 5; ++i) {
            actor.sendMessage(i);
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    });

    std::thread consumer([&actor] {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        actor.processMessages();
    });

    producer.join();
    consumer.join();

    return 0;
}

输出结果

接收到消息: 0
接收到消息: 1
接收到消息: 2
接收到消息: 3
接收到消息: 4

7. Reactive Extensions 示例代码:

#include <iostream>
#include <vector>
#include <chrono>
#include <thread>
#include "rx.hpp"

using namespace rxcpp;
using namespace rxcpp::sources;
using namespace rxcpp::operators;
using namespace rxcpp::util;

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto observable = from(nums) // 创建可观察序列
        .filter([](int n) { return n % 2 == 0; }) // 过滤偶数
        .map([](int n) { return n * n; }) // 将每个数平方
        .subscribe_on(rx::serialize_new_thread()) // 在新线程上执行上述操作
        .observe_on(rx::observe_on_event_loop()); // 切回主线程

    observable.subscribe([](int n) { std::cout << n << " "; }); // 订阅序列并输出

    std::this_thread::sleep_for(std::chrono::seconds(1)); // 等待异步操作完成

    std::cout << std::endl;

    return 0;
}

使用 subscribe_on() 操作符将序列的创建、过滤和映射操作切换到一个新的线程上执行。然后,使用 observe_on() 操作符将结果切换回主线程,并在主线程上订阅序列,并输出结果

4 16 36 64 100

8. 数据流处理框架 示例代码:

#include <iostream>
#include "rx.hpp"

using namespace rxcpp;
using namespace rxcpp::sources;
using namespace rxcpp::operators;
using namespace rxcpp::util;

int main() {
    // 创建一个整数序列
    auto values = rxcpp::observable<>::range(1, 10);

    // 对序列进行转换和过滤操作
    auto result = values
        .filter([](int n) { return n % 2 == 0; }) // 过滤偶数
        .map([](int n) { return n * n; }); // 平方

    // 订阅结果并输出
    result.subscribe(
        [](int v) { std::cout << v << " "; },
        []() { std::cout << "onCompleted" << std::endl; }
    );

    return 0;
}

使用 rxcpp::observable<>::range() 创建一个整数序列,从1到10。然后,我们使用 filter() 操作符过滤掉所有奇数,再使用 map() 操作符将剩余的偶数平方。最后,我们订阅了这个序列,并在每个值被发出时输出它的值。

这段代码演示了如何使用rxcpp来处理数据流,包括创建可观察序列、对序列进行转换和过滤操作,以及订阅结果并处理每个值。通过这种方式,我们能够以可组合的方式处理数据流,实现更加灵活和高效的数据处理逻辑。

输出结果

4 16 36 64 100 onCompleted

5. 参考文档

在C语言中,要实现异步执行且不阻塞的接口,可以使用多线程或异步回调函数。 1. 使用多线程 可以使用pthread库中的pthread_create函数创建一个新线程,将需要异步执行的任务放在新线程中执行,主线程可以继续执行其他任务,不会被阻塞。 示例代码: ```c #include <stdio.h> #include <pthread.h> void* async_task(void* arg) { // 异步执行的任务 printf("Async task is running.\n"); return NULL; } int main() { pthread_t thread; int ret = pthread_create(&thread, NULL, async_task, NULL); if (ret != 0) { printf("Failed to create thread.\n"); return 1; } printf("Main thread is running.\n"); // 等待异步任务完成 pthread_join(thread, NULL); return 0; } ``` 2. 使用异步回调函数 可以使用异步回调函数来实现异步执行,不阻塞。具体实现方式是,在调用接口时,传入一个回调函数,异步任务完成后,调用回调函数通知结果。 示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> typedef void (*async_callback)(int result); void async_task(async_callback callback) { // 异步执行的任务 sleep(3); int result = rand() % 100; callback(result); } void async_callback_func(int result) { printf("Async task is finished, result = %d.\n", result); } int main() { async_task(async_callback_func); printf("Main thread is running.\n"); return 0; } ``` 在上面的代码中,async_task函数是一个异步执行的任务,它接收一个回调函数作为参数,任务完成后调用回调函数通知结果。在主函数中,调用async_task函数时传入了一个回调函数async_callback_func。这样,在异步任务完成后,会调用async_callback_func函数通知结果。在此期间,主线程可以继续执行其他任务,不会被阻塞。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dotnet研习社

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值