C++回调与异步浅谈

在C++编程中,异步操作和回调是用于处理并发任务的两种常用方法。理解它们有助于编写高效的程序,特别是在处理I/O操作、网络通信等场景时。

先引入一段知乎上的对回调的经典阐释:

你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。

这个解释很贴切地说明了回调函数的概念和使用场景。用一段代码来更直观地阐释下这位网友的论点

#include <iostream>
#include <functional>

// 定义回调函数类型
using Callback = std::function<void()>;

// 商店类
class Store {
public:
    // 注册回调函数
    void registerCallback(Callback callback) {
        m_callback = callback;
    }

    // 当店里有货时触发回调事件
    void hasStock() {
        std::cout << "店里有货了!" << std::endl;
        // 触发回调事件
        if (m_callback) {
            m_callback();
        }
    }

private:
    Callback m_callback;
};

// 顾客类
class Customer {
public:
    // 接收到店员的电话通知
    void receiveCall() {
        std::cout << "接到店员的电话通知,前往店里取货。" << std::endl;
    }
};

int main() {
    Store store;
    Customer customer;

    // 注册回调函数
    store.registerCallback([&customer]() {
        customer.receiveCall();
    });

    // 模拟店里有货
    store.hasStock();

    return 0;
}

确实是非常直观,接下来我们用更直接的方式来浅谈回调,以及异步,以及二者的联系。

异步操作

异步操作(Asynchronous Operation)允许程序在等待某个任务完成时继续执行其他代码,而不需要阻塞或暂停整个程序的执行。C++11引入了std::asyncstd::future来支持异步编程。

例子:使用std::async进行异步操作
#include <iostream>
#include <future>
#include <thread>
#include <chrono>

// 一个模拟的长时间运行的函数
int longRunningTask() {
    std::this_thread::sleep_for(std::chrono::seconds(3)); // 模拟一个耗时3秒的任务
    return 42;
}

int main() {
    std::cout << "Starting long running task asynchronously..." << std::endl;
    
    // 使用 std::async 启动异步任务
    std::future<int> result = std::async(std::launch::async, longRunningTask);
    
    std::cout << "Doing other work while waiting for the task to complete..." << std::endl;
    
    // 主线程可以继续做其他工作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Still doing other work..." << std::endl;
    
    // 等待异步任务完成并获取结果
    int value = result.get(); // 这里会阻塞直到异步任务完成
    std::cout << "Long running task completed with result: " << value << std::endl;
    
    return 0;
}

在这个例子中,std::async启动了一个异步任务longRunningTask。主线程在等待异步任务完成的同时,可以继续执行其他操作。当需要结果时,通过result.get()获取,注意这会阻塞直到任务完成。

回调

回调(Callback)是一种通过将一个函数作为参数传递给另一个函数,并在适当的时候调用这个函数的技术。在异步编程中,回调函数通常在某个异步操作完成时被调用。

例子:使用回调进行异步操作
#include <iostream>
#include <thread>
#include <functional>

// 一个模拟的长时间运行的函数,接受一个回调函数
void longRunningTaskWithCallback(std::function<void(int)> callback) {
    std::this_thread::sleep_for(std::chrono::seconds(3)); // 模拟一个耗时3秒的任务
    int result = 42;
    callback(result); // 调用回调函数并传递结果
}

// 一个回调函数
void onTaskCompleted(int result) {
    std::cout << "Callback called with result: " << result << std::endl;
}

int main() {
    std::cout << "Starting long running task with callback..." << std::endl;
    
    // 使用 std::thread 启动异步任务,并传递回调函数
    std::thread task(longRunningTaskWithCallback, onTaskCompleted);
    
    std::cout << "Doing other work while waiting for the task to complete..." << std::endl;
    
    // 主线程可以继续做其他工作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Still doing other work..." << std::endl;
    
    // 等待异步任务完成
    task.join(); // 等待线程完成
    
    return 0;
}

在这个例子中,我们使用std::thread启动一个异步任务,并将一个回调函数onTaskCompleted传递给它。longRunningTaskWithCallback函数在任务完成后调用回调函数并传递结果。主线程在等待任务完成的同时,可以继续执行其他操作。

总结

  • 异步操作通过不阻塞主线程来提高程序效率。C++中的std::asyncstd::future提供了简单的异步操作支持。
  • 回调通过将一个函数作为参数传递,并在异步操作完成时调用这个函数,提供了一种灵活的处理异步任务完成的方法。

这两种技术在C++中都非常有用,具体选择哪种方法取决于你的应用场景和需求。

异步操作和回调在很多应用场景中都非常有用,特别是在处理I/O密集型任务、网络通信、用户界面响应以及并行计算时。以下是一些具体的应用场景和需求,可以利用这些技术来提高效率和性能。

异步操作的应用场景

  1. 文件I/O操作

    • 当程序需要读取或写入大量数据时,使用异步I/O操作可以避免阻塞主线程,从而提高应用程序的响应性。
    • 例如,一个媒体播放器在播放音频文件时,可以异步加载音频数据,避免播放中断。
  2. 网络请求

    • 网络通信通常涉及长时间的等待,如HTTP请求、数据库查询等。使用异步操作可以在等待响应的同时执行其他任务。
    • 例如,一个网页浏览器在请求网页内容时可以异步加载资源,用户界面不会被阻塞。
  3. 并行计算

    • 处理大规模数据或执行计算密集型任务时,可以将任务分割成多个子任务,并行执行以提高处理速度。
    • 例如,图像处理程序可以将图像分割成多个部分,并行处理这些部分以加速图像渲染。
  4. 游戏开发

    • 游戏中需要同时处理多个任务,如用户输入、物理计算、渲染等,使用异步操作可以提高游戏的响应速度和流畅度。
    • 例如,游戏引擎在加载场景时可以异步加载资源,避免游戏画面卡顿。

回调的应用场景

  1. 事件驱动编程

    • 在GUI应用程序中,用户界面通常是事件驱动的,通过回调函数处理用户交互事件(如按钮点击、鼠标移动等)。
    • 例如,Windows应用程序使用回调函数处理窗口消息,如WM_PAINTWM_KEYDOWN等。
  2. 异步任务的完成通知

    • 当异步任务完成时,通过回调函数通知主线程,从而触发后续操作。
    • 例如,一个下载管理器在文件下载完成后,通过回调函数通知用户下载完成,并更新用户界面。
  3. 插件和扩展机制

    • 应用程序可以通过回调函数提供插件或扩展点,允许外部代码注入行为。
    • 例如,Web服务器可以通过回调机制支持不同的处理模块,处理不同类型的请求。
  4. 信号和槽机制

    • 在一些框架(如Qt)中,回调函数常用于信号和槽机制,当某个事件发生时,自动调用相应的回调函数。
    • 例如,一个Qt应用程序可以通过信号和槽机制处理按钮点击事件,执行相应的业务逻辑。

具体例子

异步文件读取示例
#include <iostream>
#include <fstream>
#include <future>
#include <vector>

// 异步读取文件内容
std::vector<char> readFileAsync(const std::string& filename) {
    std::ifstream file(filename, std::ios::binary | std::ios::ate);
    if (!file) {
        throw std::runtime_error("Failed to open file");
    }
    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);
    std::vector<char> buffer(size);
    if (file.read(buffer.data(), size)) {
        return buffer;
    }
    throw std::runtime_error("Failed to read file");
}

int main() {
    std::string filename = "example.txt";
    
    // 异步读取文件
    std::future<std::vector<char>> future = std::async(std::launch::async, readFileAsync, filename);
    
    std::cout << "Reading file asynchronously..." << std::endl;
    
    // 在等待文件读取的同时,可以做其他工作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Doing other work..." << std::endl;
    
    // 获取文件内容
    try {
        std::vector<char> content = future.get();
        std::cout << "File content read successfully. Size: " << content.size() << " bytes." << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}

回调处理网络请求示例

#include <iostream>
#include <thread>
#include <functional>
#include <chrono>

// 模拟网络请求函数,接受一个回调函数
void asyncNetworkRequest(std::function<void(const std::string&)> callback) {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟网络延迟
    std::string response = "Server response data";
    callback(response); // 调用回调函数并传递响应数据
}

// 回调函数
void onRequestCompleted(const std::string& response) {
    std::cout << "Network request completed with response: " << response << std::endl;
}

int main() {
    std::cout << "Sending network request..." << std::endl;
    
    // 异步发送网络请求,并传递回调函数
    std::thread requestThread(asyncNetworkRequest, onRequestCompleted);
    
    std::cout << "Doing other work while waiting for network response..." << std::endl;
    
    // 主线程可以继续做其他工作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Still doing other work..." << std::endl;
    
    // 等待网络请求完成
    requestThread.join(); // 等待线程完成
    
    return 0;
}

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值