回调函数,同步回调与异步回调

一,回调函数:

回调函数(Callback Function)是一种特殊的函数,它作为参数传递给另一个函数(通常是高层函数或库函数),并在需要的时候由那个函数执行。回调函数的主要作用是允许用户定义的代码在特定事件或条件发生时被执行,而不需要显式地调用它。这种方式增加了代码的灵活性和模块化。
回调函数的特点

  1. 作为参数传递:回调函数是作为参数传递给另一个函数的。这允许高层函数在适当的时候调用它,而不是由调用者直接调用。
  2. 非同步执行:在异步编程中,回调函数特别有用,因为它们允许程序在等待某些事件(如网络请求、文件IO操作等)完成时继续执行其他任务。一旦事件完成,回调函数就会被调用,以处理结果或更新状态。
  3. 灵活性:通过传递不同的回调函数,可以很容易地改变程序的行为,而不需要修改高层函数的代码。
  4. 嵌套和链式调用:回调函数可以嵌套使用,也可以链式调用,这允许创建复杂的逻辑流和异步流程控制
C++回调函数示例:

在C++中,当你在多个文件之间使用回调函数并需要传递参数时,你通常需要确保回调函数的原型在多个文件中都是可见的(通常是通过头文件),并且正确地管理回调函数的注册和调用过程。
这里有一个基本的示例来说明如何在多个文件中使用带参数的回调函数。
三个步骤:1. 定义回调函数类型,2. 实现回调函数管理器 3. 定义回调函数

步骤 1: 定义回调函数类型

首先,在一个头文件中定义回调函数的类型。这个类型通常是一个函数指针类型,指向具有特定签名(即参数和返回类型)的函数。
Callback.h

#ifndef CALLBACK_H
#define CALLBACK_H

// 定义回调函数类型
typedef void (*CallbackFunc)(int);

// 一个外部函数,用于注册和调用回调函数
class CallbackManager {
public:
    static void setCallback(CallbackFunc func);
    static void doSomething(int data);

private:
    static CallbackFunc callbackFunc;
};
#endif // CALLBACK_H
步骤 2: 实现回调函数管理器

然后,在一个源文件中实现回调函数管理器。这个管理器将包含一个静态函数指针,用于存储回调函数的地址,并提供设置回调函数和调用它的方法。
Callback.cpp

#include "Callback.h"

CallbackFunc CallbackManager::callbackFunc = nullptr;

void CallbackManager::setCallback(CallbackFunc func) {
    callbackFunc = func;
}

void CallbackManager::doSomething(int data) {
    if (callbackFunc != nullptr) {
        callbackFunc(data);
    }
}
步骤 3: 定义回调函数

现在,你可以在另一个源文件中定义一个或多个符合CallbackFunc签名的函数。
Main.cpp

#include <iostream>
#include "Callback.h"

// 回调函数实现
void myCallback(int data) {
    std::cout << "Callback received: " << data << std::endl;
}

int main() {
    // 注册回调函数
    CallbackManager::setCallback(myCallback);

    // 调用doSomething,它将触发回调函数
    CallbackManager::doSomething(42);

    return 0;
}
综述

这个示例展示了如何在多个文件中使用回调函数,并通过回调函数传递参数。首先,在头文件中定义回调函数的类型和一个管理器类,用于存储和调用回调函数。然后,在源文件中实现这个管理器类,并在另一个源文件中定义和注册回调函数。最后,在main函数中,你可以调用管理器的方法来触发回调函数。
这种方法的好处是,你可以在不同的文件中定义和使用回调函数,同时保持代码的模块化和可重用性。

回调函数事件驱动C++代码示例:

在C++中,实现一个基于回调函数的事件驱动系统可以涉及到多个组件,包括事件类型、事件监听器(回调函数的持有者)、事件分发器(负责将事件派发给相应的监听器)等。以下是一个简化的C++代码示例,展示了如何实现这样一个系统。
首先,我们定义一个事件类型枚举和一个用于存储回调函数的类型(通常使用std::function):

#include <iostream>
#include <functional>
#include <unordered_map>

// 事件类型枚举
enum class EventType {
    CLICK,
    TIMER_EXPIRED,
    QUIT
};

// 回调函数类型,使用std::function以支持多种可调用对象
using EventCallback = std::function<void(const std::string& data)>;

// 事件管理器类
class EventManager {
private:
    // 使用unordered_map来存储事件类型和回调函数的映射
    std::unordered_map<EventType, std::vector<EventCallback>> callbacks;

public:
    // 注册回调函数
    void registerCallback(EventType type, EventCallback cb) {
        callbacks[type].push_back(cb);
    }

    // 触发事件
    void triggerEvent(EventType type, const std::string& data = "") {
        if (callbacks.find(type) != callbacks.end()) {
            for (const auto& cb : callbacks[type]) {
                cb(data);
            }
        }
    }

    // ... 其他可能的方法,如注销回调等
};

// 示例回调函数
void onClick(const std::string& data) {
    std::cout << "Event CLICK triggered with data: " << data << std::endl;
}

void onTimerExpired(const std::string& data) {
    std::cout << "Event TIMER_EXPIRED triggered with data: " << data << std::endl;
}

int main() {
    EventManager em;

    // 注册回调函数
    em.registerCallback(EventType::CLICK, onClick);
    em.registerCallback(EventType::TIMER_EXPIRED, onTimerExpired);

    // 触发事件
    em.triggerEvent(EventType::CLICK, "Button 1 clicked");
    em.triggerEvent(EventType::TIMER_EXPIRED, "5 seconds expired");

    // 假设在某个时刻,我们想要退出程序
    // em.triggerEvent(EventType::QUIT); // 可以在这里处理退出逻辑,但本例中没有展示

    return 0;
}

在这个示例中,EventManager类负责存储事件和回调函数的映射,并提供registerCallback方法来注册回调函数,以及triggerEvent方法来触发事件。每个事件类型可以关联多个回调函数,因此我们将它们存储在std::vector中。
EventCallback是一个std::function类型,它可以接受任何可调用对象,包括普通函数、Lambda表达式、函数对象等。在这个例子中,我们简单地使用了一个字符串data作为事件的附加数据,但你可以根据需要传递更复杂的数据结构。
在main函数中,我们创建了一个EventManager实例,注册了两个回调函数,并触发了它们对应的事件。输出将显示每个事件被触发时调用的回调函数的输出。
请注意,这个示例没有实现注销回调函数的功能,但在实际应用中,你可能需要提供一种方法来注销不再需要的回调函数,以避免内存泄漏或不必要的调用。这通常可以通过在EventManager中添加一个unregisterCallback方法来实现,该方法接受事件类型和要注销的回调函数作为参数,并从相应的映射中移除它。

同步回调和异步回调

同步回调和异步回调是两种不同的编程模式,它们主要用于处理事件或异步操作的结果。

同步回调

同步回调指的是在调用一个函数后,程序会等待这个函数执行完毕并返回结果。在这个过程中,程序的控制权不会被释放,也就是说,直到回调执行完成,程序才会继续执行后续的代码。
示例代码
假设有一个同步回调的例子,其中 doSomething 函数接受一个回调函数 onComplete 并在完成后调用它。

#include <iostream>
// 定义回调函数的类型
using Callback = void (*)(int);
void doSomething(Callback onComplete) {
    // 模拟一些耗时的操作
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    // 调用回调函数
    onComplete(42);
}
int main() {
    auto callback = [](int result) {
        std::cout << "Callback received result: " << result << std::endl;
    };
    doSomething(callback);
    std::cout << "This line will be printed after the callback is executed." << std::endl;
    return 0;
}
异步回调

异步回调则是在调用一个函数后,程序不会等待这个函数执行完毕。相反,程序会立即继续执行后续的代码,而当异步操作完成时,会通过回调函数通知主程序。
示例代码
同样的例子,但这次 doSomethingAsync 是异步的,它会立即返回,并在操作完成后调用回调函数。

#include <iostream>
#include <thread>
#include <future>
// 定义回调函数的类型
using AsyncCallback = void (*)(int);
void doSomethingAsync(AsyncCallback onComplete) {
    std::thread([=]() {
        // 模拟一些耗时的操作
        std::this_thread::sleep_for(std::chrono::seconds(2));
        
        // 调用回调函数
        onComplete(42);
    }).detach();
}
int main() {
    auto callback = [](int result) {
        std::cout << "Async callback received result: " << result << std::endl;
    };
    doSomethingAsync(callback);
    std::cout << "This line will be printed immediately." << std::endl;
    // 等待异步操作完成
    std::this_thread::sleep_for(std::chrono::seconds(3));
    return 0;
}
注意事项
  1. 同步回调:
  • 通常用于简单的同步操作。
  • 可能会导致程序阻塞,如果操作耗时较长。
  1. 异步回调:
  • 适用于长时间运行的任务或需要等待外部资源的情况。
  • 需要注意线程安全问题,例如确保回调函数不会访问共享资源导致竞态条件。
  • 异步回调通常需要额外的机制来处理错误和结果。
    以上就是同步回调和异步回调的基本概念和示例。在实际开发中,选择哪种模式取决于具体的应用场景和性能需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值