什么是回调函数?

什么是回调函数?

在编程中,总是会听到回调函数这个字眼,但回调函数究竟是什么尼?,维基百科的解释是这样子的:

In computer programming, a callback, also known as a “call-after”[1] function, is any executable code that is passed as an argument to other code; that other code is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous callback, or it might happen at a later point in time as in an asynchronous callback.

中文翻译就是:

在计算机编程中,回调(也称为“ call-after ”函数)是作为参数传递给其他代码的任何可执行代码。其他代码可以在给定时间回调(执行)参数。该执行可以像在同步回调中那样立即执行,也可以在异步回调中稍后的某个时间点执行。

下面我们举个栗子:
假设你有一台洗衣机和一台烘干机,你的衣服需要洗衣机洗完之后,再拿去烘干机中进行烘干,下面定义两个函数。

#include<iostream>

void washing()
{
    std::cout<<"洗衣服"<<std::endl;
}

void drying()
{
    std::cout<<"烘干衣服"<<std::endl;
}

int main()
{
    washing();
    drying();
    return 0;
}

上面的步骤是先把衣服拿去洗,洗完后立马拿去烘干。但是这期间你需要守着洗衣机,因为洗完衣服才能立马拿去烘干。洗衣服时间一般需要30分钟,你不想一直盯着洗衣机30分钟,也没有人会想一直盯着洗衣机看30分钟,因为这样子很傻。

因此,接下来轮到回调函数出场了,接着举栗:
假设你有一台洗烘一体机,你的衣服洗完之后会自动进行烘干,函数定义如下:

#include<iostream>
typedef void (*callback)(void);
void drying();

void washing(callback func)
{
    std::cout<<"洗衣服"<<std::endl;
    func();
}

void drying()
{
    std::cout<<"烘干衣服"<<std::endl;
}

int main()
{
    washing(drying);
    return 0;
}

大家可以发现,washing比之前多了一个参数,这个参数是一个函数指针,并在洗完衣服之后执行该函数。在main函数中把drying这个函数给传进去了,此时这个函数我们就称为callback,中文称之为回调

这样子你就不用向之前那样盯着衣服洗完,再立马拿去烘干了,因为你把洗完衣服要做的事情告诉了washing这个函数,这个函数执行完洗衣服这个动作后,会立马进行烘干。不过这个还是一个同步执行的过程,因为在washing没执行完之前,main函数什么也做不了。

异步回调

洗衣服的时间那么长,我可以不可以利用洗衣服的时间做点其他事情尼,那么就要用到异步调用了,所以的异步就是不等任务执行完,直接执行下一个任务。异步回调 其实就是使用了异步调用的方式。

下面是使用异步调用的一段代码,我们有两个函数,其中washing耗时5s,do_homework函数耗时3s,然后我们在main函数中对其进行异步调用,并且对整个main函数执行时间进行了记录,看看异步耗时需要多久。

#include<iostream>
#include <future>
#include <unistd.h>
#include <chrono>

typedef std::chrono::steady_clock                STEADY_CLOCK;
typedef std::chrono::steady_clock::time_point    TIME_POINT;

void print_diff_time(const TIME_POINT& t1, const TIME_POINT& t2)
{
    std::chrono::duration<double, std::milli> dTimeSpan = std::chrono::duration<double,std::milli>(t2-t1);

    std::cout << "time span : " << dTimeSpan.count() << "ms\n";  
}

int washing()
{
    std::cout<<"开始洗衣服"<<std::endl;
    sleep(5);
    std::cout<<"花了5秒衣服洗完了"<<std::endl;
    return 0;
}

int do_homework()
{
    std::cout<<"开始做作业"<<std::endl;
    sleep(3);
    std::cout<<"花了3秒作业做完了"<<std::endl;
    return 1;
}

int main()
{
    TIME_POINT start_time = STEADY_CLOCK::now();
    
    std::cout<<"main function start"<<std::endl;
    
    std::future<int> result = std::async(washing);
    std::future<int> result2 = std::async(do_homework);
    
    std::cout<<"continue......!"<<std::endl;

    std::cout << result.get() << std::endl;
    std::cout << result2.get() << std::endl;
    
    std::cout << "main function finish!" << std::endl;
    
    TIME_POINT end_time = STEADY_CLOCK::now();
    print_diff_time(start_time, end_time);
}

执行结果如下:

main function start
continue......!
开始洗衣服
开始做作业
花了3秒作业做完了
花了5秒衣服洗完了
0
1
main function finish!
time span : 5000.86ms

若是同步调用,则需要耗时8s左右,通过异步调用我们可以执行到washing()函数时迅速返回,继续执行下一个函数do_homework,因此耗时只需要5s左右。

下面将上面的代码改成异步回调的方式,如下:

#include<iostream>
#include <future>
#include <unistd.h>
#include <chrono>

typedef std::chrono::steady_clock                STEADY_CLOCK;
typedef std::chrono::steady_clock::time_point    TIME_POINT;
typedef int (*callback)(void);

void print_diff_time(const TIME_POINT& t1, const TIME_POINT& t2)
{
    std::chrono::duration<double, std::milli> dTimeSpan = std::chrono::duration<double,std::milli>(t2-t1);

    std::cout << "time span : " << dTimeSpan.count() << "ms\n";  
}

int washing(callback call_back)
{
    std::cout<<"开始洗衣服"<<std::endl;
    std::future<int> result = std::async(std::launch::async, call_back);
    sleep(5);
    std::cout<<"花了5秒衣服洗完了"<<std::endl;
    std::cout << result.get() << std::endl;
    return 0;
}

int do_homework()
{
    std::cout<<"开始做作业"<<std::endl;
    sleep(3);
    std::cout<<"花了3秒作业做完了"<<std::endl;
    return 1;
}

int main()
{
    TIME_POINT start_time = STEADY_CLOCK::now();
    
    std::cout<<"main function start"<<std::endl;
    
    //第三个参数是washing的入参
    std::future<int> result = std::async(std::launch::async, washing, do_homework); 
    
    std::cout<<"continue......!"<<std::endl;    

    std::cout << result.get() << std::endl;
    
    std::cout << "main function finish!" << std::endl;
    
    TIME_POINT end_time = STEADY_CLOCK::now();
    print_diff_time(start_time, end_time);
}

执行结果如下:

main function start
continue......!
开始洗衣服
开始做作业
花了3秒作业做完了
花了5秒衣服洗完了
1
0
main function finish!
time span : 5000.88ms

在代码中,我们把do_homework做作业这个函数作为参数传入washing中,这样子我们可以在调用washing的同时,do_homework也会在内部异步调用,且不阻塞washing内其他语句的执行,最后再将执行完成的结果返回到washing中打印出来。

总结

最后,简单总结一下,同步的代码看起来比较直观,编码和维护都比较容易,但是效率低,效率低在绝大多数情况下都是不能忍受的。而异步效率高,但是通过callback这种形式逻辑容易被代码割裂,代码比较不直观。不过C++20已经有了协程,有兴趣的同学可以去研究一下,通过协程既可以实现异步,代码看起来也比较直观明了。

更多精彩内容,可以关注爱打码公众号,每月不定时更新。
爱打码 公众号

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值