通俗理解“回调函数”

回调机制

模块间的调用

本部分摘自https://www.cnblogs.com/xrq730/p/6424471.html

在一个应用系统中,无论使用何种语言开发,必然存在模块之间的调用,调用的方式分为几种:

(1)同步调用

同步调用是最基本并且最简单的一种调用方式,类A的方法a()调用类B的方法b(),一直等待b()方法执行完毕,a()方法继续往下走。这种调用方式适用于方法b()执行时间不长的情况,因为b()方法执行时间一长或者直接阻塞的话,a()方法的余下代码是无法执行下去的,这样会造成整个流程的阻塞。

image

(2)异步调用

image

异步调用是为了解决同步调用可能出现阻塞,导致整个流程卡住而产生的一种调用方式。类A的方法方法a()通过新起线程的方式调用类B的方法b(),代码接着直接往下执行,这样无论方法b()执行时间多久,都不会阻塞住方法a()的执行。

但是这种方式,由于方法a()不等待方法b()的执行完成,在方法a()需要方法b()执行结果的情况下(视具体业务而定,有些业务比如启异步线程发个微信通知、刷新一个缓存这种就没必要),必须通过一定的方式对方法b()的执行结果进行监听。

在Java中,可以使用Future+Callable的方式做到这一点,具体做法可以参见我的这篇文章Java多线程21:多线程下其他组件之CyclicBarrier、Callable、Future和FutureTask。

(3)回调

image

最后是回调,回调的思想是:

类A的a()方法调用类B的b()方法 类B的b()方法执行完毕主动调用类A的callback()方法 这样一种调用方式组成了上图,也就是一种双向的调用方式。

回调实例:Tom做题

数学老师让Tom做一道题,并且Tom做题期间数学老师不用盯着Tom,而是在玩手机,等Tom把题目做完后再把答案告诉老师。

1 数学老师需要Tom的一个引用,然后才能将题目发给Tom。

2 数学老师需要提供一个方法以便Tom做完题目以后能够将答案告诉他。

3 Tom需要数学老师的一个引用,以便Tom把答案给这位老师,而不是隔壁的体育老师。

回调接口,可以理解为老师接口

    //回调指的是A调用B来做一件事,B做完以后将结果告诉给A,这期间A可以做别的事情。
    //这个接口中有一个方法,意为B做完题目后告诉A时使用的方法。
    //所以我们必须提供这个接口以便让B来回调。
    //回调接口,
    public interface CallBack {
        void tellAnswer(int res);
    }

通俗理解“回调函数”

我们先来看看回调的英文定义:

A callback is a function that is passed as an argument to another function and is executed after its parent function has completed。

字面上的理解,回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调。

其实也很好理解对吧,回调,回调,就是回头调用的意思。主函数的事先干完,回头再调用传进来的那个函数。但是以前看过很多博客,他们总是将回调函数解释的云里雾里,很高深的样子。

举一个别人举过的例子:约会结束后你送你女朋友回家,离别时,你肯定会说:“到家了给我发条信息,我很担心你。” 对不,然后你女朋友回家以后还真给你发了条信息。小伙子,你有戏了。其实这就是一个回调的过程。你留了个参数函数(要求女朋友给你发条信息)给你女朋友,然后你女朋友回家,回家的动作是主函数。她必须先回到家以后,主函数执行完了,再执行传进去的函数,然后你就收到一条信息了。

现在基本理解回调函数的意思了吧。不理解的话没关系,我们用代码说话。

复制代码
//定义主函数,回调函数作为参数
function A(callback) {
callback();
console.log(‘我是主函数’);
}

//定义回调函数
function B(){
setTimeout(“console.log(‘我是回调函数’)”, 3000);//模仿耗时操作
}

//调用主函数,将函数B传进去
A(B);

//输出结果
我是主函数
我是回调函数
复制代码
  上面的代码中,我们先定义了主函数和回调函数,然后再去调用主函数,将回调函数传进去。

定义主函数的时候,我们让代码先去执行callback()回调函数,但输出结果却是后输出回调函数的内容。这就说明了主函数不用等待回调函数执行完,可以接着执行自己的代码。所以一般回调函数都用在耗时操作上面。比如ajax请求,比如处理文件等。


P2

最近经常看到“回调函数”这个概念,但不是很理解。为了弄明白回调函数,提出了以下三个问题:

1.什么是回调函数

回调函数就是一个通过指针调用的函数,如果你把函数指针作为参数传给另一个函数,当这个指针被用来调用所指向的函数时,我们就说这是回调函数。

通俗理解:回调函数和普通函数没有任何区别,只是函数调用时略有区别。一般调用普通函数时,直接写函数名即可;但是在调用回调函数时,是把它作为参数传递给另一个函数。

引用网上用生活场景解释回调函数的一个例子:

比如你显示器不亮了,你不知道怎么弄,那你就问在外地干IT的大表哥,你大表哥告诉你该怎么弄,然后需要你自己来操作。你大表哥知道怎么弄,但却不能自己操作,而是由你来操作。你大表哥告诉你修机器的方法就是回调函数。

2.为什么要用回调函数

回调函数可以把调用者与被调用者分开,调用者不关心谁是被调用者,只需要知道存在一个具有特定原型和限制条件的被调用函数。

从本质上讲,回调函数都是从“你想让别人执行你的代码,而你又不能动别人的代码”这种需求下产生的。因此,只能讲自己实现的函数作为参数传递给别人的函数,这样别人的函数在特定的时候会执行自己的函数。

在面向对象的编程中,回调函数可以让模块间解耦。比如,在一个下载系统中有一个文件下载模块和一个下载文件当前进度显示模块,系统要求实时的显示文件的下载进度。但显示模块不知道也不应该知道下载模块所下载文件的下载进度(面向对象设计的封装性,模块间要解耦,模块内要内聚),文件下载进度是只有下载模块才知道的事情,此时的解决办法是,给下载模块传递一个函数指针作为回调函数驱动显示模块的显示进度。

3.如何使用回调函数
根据以上的理解,自己写了一个简单的回调函数:

 
  
 
  
 
  
简单的说,回调函数,当你调用了一个函数,但是这个函数仅仅是算法的一部分,你还需要自己定义一部分算法,该函数会自动调用你自定义的“回调函数”。

如下例子:

比如你要枚举所有的窗口。可以调用EnumWindows函数。但是,EnumWindows仅仅是将窗口枚举出来,至于如何处理则需要你来定义。

就是说,当你调用EnumWindow,要指定一个回调函数,来告诉EnumWindow,当她枚举到窗口的时候,应该怎么处理这个窗口。
 
  
 
  
https://zhidao.baidu.com/question/395318887552374965.html?fr=iks&word=�ص���ͨ�����&ie=gbk
回调函数就是你写好一个函数,让预先写好的系统来调用。你去调用系统的函数,是直调。让系统调用你的函数,就是回调。但假如满足于这种一句话结论,是不会真正明白的。

回调函数可以看成,让别人做事,传进去的额外信息。

比如,A 让 B 做事,根据粒度不同,可以理解成 A 函数调用 B 函数,或者 A 类使用 B 类,或者 A 组件使用 B 组件等等。反正就是 A 叫 B 做事。

当 B 做这件事情的时候,自身的需要的信息不够,而 A 又有。就需要 A 从外面传进来,或者 B 做着做着主动向外面申请。对于 B 来说,一种被动得到信息,一种是主动去得到信息,有人给这两种方式术语,叫信息的 push,和信息的 pull。
<think>好的,用户之前已经问过VxWorks中如何通过回调或钩子函数在某个函数执行时触发另一个函数的问题,现在他们进一步询问VxWorks回调函数的具体情况。我需要详细解释回调函数的概念、使用场景、实现步骤以及注意事项。 首先,用户可能是在开发VxWorks应用时遇到了需要异步处理的问题,比如处理硬件中断或者定时任务。他们可能对回调机制不太熟悉,需要了解如何在VxWorks中正确使用回调函数。之前的回答已经对比了回调和钩子函数,现在需要深入回调部分。 需要明确回调函数的定义,它是如何通过函数指针传递并被调用的。接下来,应该举一些典型的应用场景,比如硬件中断处理、定时器、任务同步机制等。这些场景能帮助用户理解何时使用回调函数。 然后,详细说明实现步骤,包括定义回调函数、注册到事件源、事件触发时的调用过程。这里需要具体的代码示例,比如定时器的使用,消息队列的设置,这样用户能更直观地理解如何操作。同时,注意代码的正确性,比如VxWorks特有的API,如timer_create、msgQSend等。 另外,要强调注意事项,比如实时性要求、上下文安全、资源管理和错误处理。这些是用户在实际开发中容易忽视的地方,特别是VxWorks作为实时系统,回调函数的执行时间必须严格控制,避免影响系统性能。 还要考虑用户可能的疑问,比如回调函数和普通函数调用的区别,为何选择回调而不是其他机制。需要解释回调的异步特性,以及它在解耦和模块化设计中的优势。 最后,总结回调函数的适用场景,帮助用户判断是否适合他们的需求。可能用户需要的是一个轻量级的、事件驱动的解决方案,而回调正好符合这一点。 需要确保回答结构清晰,分点说明,代码示例正确,并且语言通俗易懂,避免过于技术化的术语,但又要准确。同时,检查之前的回答是否有遗漏,确保不重复但补充详细信息。</think>好的!在VxWorks中,**回调函数(Callback Function)** 是一种通过函数指针实现的机制,允许在特定事件(如中断触发、定时器到期或任务间通信)发生时自动执行预定义的函数。以下是针对VxWorks回调函数的详细说明: --- ### 一、回调函数的核心概念 - **本质**:将函数指针传递给某个事件源,当该事件发生时,系统自动调用此函数。 - **目的**:实现异步响应,解耦事件触发与处理逻辑。 - **典型场景**: - 硬件中断服务程序(ISR)的后续处理 - 定时器到期后的操作 - 消息队列/信号量到达时的任务唤醒 --- ### 二、回调函数的使用步骤(以定时器为例) #### 1. **定义回调函数** ```c void myTimerCallback(int arg) { printf("Timer expired! Argument: %d\n", arg); // 在此添加自定义逻辑(例如置位标志、发送信号量等) } ``` - **要求**:函数签名需与事件源期望的格式匹配(例如参数类型、返回值)。 #### 2. **注册回调到事件源** ```c #include <timers.h> timer_t timerId; struct sigevent event; // 配置事件类型为回调函数 event.sigev_notify = SIGEV_CALLBACK; event.sigev_notify_function = (void (*)(union sigval))myTimerCallback; // 传递参数(可通过sigev_value携带数据) event.sigev_value.sival_int = 42; // 创建定时器并绑定回调 timer_create(CLOCK_REALTIME, &event, &timerId); ``` #### 3. **启动事件源并触发回调** ```c struct itimerspec timerSpec; timerSpec.it_value.tv_sec = 1; // 1秒后首次触发 timerSpec.it_value.tv_nsec = 0; timerSpec.it_interval.tv_sec = 0; // 单次触发(不重复) timerSpec.it_interval.tv_nsec = 0; timer_settime(timerId, 0, &timerSpec, NULL); ``` - 当定时器到期时,系统会自动调用`myTimerCallback`。 --- ### 三、VxWorks中回调的典型应用场景 #### 1. **硬件中断处理** ```c // 定义中断服务程序(ISR)和回调 void isrCallback(void* arg) { // 在ISR上下文中快速处理(不可阻塞) printf("Interrupt occurred!\n"); } // 注册中断向量 intConnect(INUM_TO_IVEC(irqNum), isrCallback, NULL); ``` #### 2. **消息队列通知** ```c MSG_Q_ID msgQId = msgQCreate(10, sizeof(int), MSG_Q_FIFO); // 定义消息到达回调 void msgCallback(MSG_Q_ID msgQId) { int data; msgQReceive(msgQId, (char*)&data, sizeof(int), NO_WAIT); printf("Received data: %d\n", data); } // 绑定回调到消息队列 msgQNotifyAdd(msgQId, (FUNCPTR)msgCallback); ``` #### 3. **信号量释放响应** ```c SEM_ID semId = semBCreate(SEM_Q_FIFO, SEM_EMPTY); void semCallback(SEM_ID semId) { printf("Semaphore released!\n"); // 执行后续操作(例如唤醒任务) } semNotifyTake(semId, (FUNCPTR)semCallback); ``` --- ### 四、回调函数的注意事项 1. **实时性要求**: - 回调函数应尽量简短,避免阻塞(尤其在ISR上下文中)。 - 若需复杂操作,可触发任务级处理(例如通过信号量唤醒任务)。 2. **上下文安全**: - **中断上下文**(如硬件ISR回调):禁止使用`printf`、`malloc`等可能阻塞的API。 - **任务上下文**(如定时器回调):可调用大部分系统API,但仍需控制执行时间。 3. **资源管理**: - 确保回调函数与事件源的生命周期匹配(例如及时注销不再需要的回调)。 4. **错误处理**: ```c if (timer_create(CLOCK_REALTIME, &event, &timerId) == ERROR) { printf("Timer creation failed: %s\n", strerror(errno)); } ``` --- ### 五、回调函数 vs 普通函数调用 | **对比项** | **回调函数** | **普通函数调用** | |------------------|----------------------------------|------------------------------| | **调用时机** | 由事件触发(异步) | 开发者显式调用(同步) | | **控制权** | 事件源控制 | 调用者控制 | | **典型用途** | 中断响应、异步通知 | 顺序逻辑、模块化代码 | | **耦合性** | 低(通过接口解耦) | 高(直接依赖函数实现) | --- ### 六、总结 在VxWorks中使用回调函数的核心逻辑是:**将自定义函数与系统事件绑定,实现异步响应**。关键在于: 1. 正确定义回调函数签名。 2. 通过VxWorks提供的API(如`timer_create`、`msgQNotifyAdd`)注册回调。 3. 确保回调函数符合实时性和上下文安全要求。 如果需要更底层的系统行为拦截(如监控所有任务创建),可结合钩子函数(Hook Function)实现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值