1.概念:
回调就是把一段可执行的代码像参数传递那样传递给其他的代码,而这段代码会在某个时刻被调用执行。一段代码不好传递,我们就将其封装成函数lambda表达式,类成员函数等,然后将函数的地址当作参数传递到另外一个函数,被传递的就是回调函数,被调用的时机可以是立刻,也可以是过段时间,那么这也叫做同步回调和异步回调。
2.函数指针:
这个涉及到了把函数当作参数传递,这个就涉及到了函数指针,那么我们来看看什么是函数指针。
int fun(int a,int b)
{
cout<<"hello,world"<<endl;
return a+b;
}
int main()
{
int (*p1)(int a,int b) = NULL;
p1 = fun;
cout<<p1(2,3);
}
我们可以从这个例子总结下简单的语法格式:函数类型 (*函数名)(函数参数);
这里的函数类型,和函数参数需要和赋值的函数保持一致。上述例子中的函数参数(int a,int b)可以选择不保留a ,b;
3.回调函数:
3.1简单回调:
当我们了解了函数指针的使用后,我们尝试将函数指针当成一个参数传递给另外一个函数去调用它。
void callback()
{
std::cout << "Callback function called.\n";
}
void use_callback(void (*p)())
{
p();
}
int main()
{
auto lambda = []()
{ std::cout << "Lambda function called.\n"; };
void (*p)() = nullptr;
p = callback;
use_callback(lambda);
use_callback(p);
return 0;
}
上述代码中我们可以看到函数指针p被当成参数传递到了use_callback函数中去了,同样的我们也可以使用lambda表达式(本质匿名函数),函数名字(函数的地址)作为参数传递进去。下面的代码就是将函数名称作为参数传递的。
#include <iostream>
using namespace std;
void callback()
{
std::cout<<"hello,world\n"<<endl;
}
void use_callback(void (*func)())
{
func();
}
int main()
{
use_callback(callback);
return 0;
}
3.2functional简单使用:
我们可以看到,作为参数来看可调用对象是非常多的,函数名称,lambda表达式,函数指针等,我们在编写回调函数的时候是不知道会传入什么参数进来的,这样在我们试图使用统一的方式保持,或传递一个可调用对象时,会非常的繁琐。为了方便统一的管理,C++11引入了std::functional可调用对象包装器来统一的处理函数,函数对象,lambda表达式等,并允许保持和延迟执行它们。
我们先看看functional的简单使用:
#include <functional>
void callback()
{
std::cout << "Callback function called.\n";
}
void callback1(int a)
{
std::cout << "Callback function called with parameter: \n" << a<<std::endl;
}
int main()
{
std::function<void()> f = callback;
std::function<void(int)> f1 = callback1;
f();
f1(3);
return 0;
}
functional的尖括号中要保持和对应赋值函数的类型,参数一致。
functional作为形参的使用:
#include <iostream>
#include <functional>
void callback()
{
std::cout << "Callback function called.\n";
}
void use_callback(std::function<void()> f)
{
f();
}
int main()
{
auto lambda = []()
{ std::cout << "Lambda function called.\n"; };
use_callback(callback);
use_callback(lambda);
return 0;
}
虽然在调用方式上没啥区别,但是多了一个统一的封装和存储管理。
3.2异步回调:
#include <iostream>
#include <thread>
#include <chrono>
#include <future>
int asynccallback(int time_s)
{
std::cout<<"asynccallback function do some logical\n";
std::this_thread::sleep_for(std::chrono::seconds(time_s));
return time_s*2;
}
int main()
{
std::promise<int> p1;
std::future<int> f1 = std::async(std::launch::async, asynccallback, 2);
std::thread([](std::future<int>& f, std::promise<int>& p) {
p.set_value(f.get());
},std::ref(f1),std::ref(p1)).detach();
std::cout << "waiting...the result \n" <<p1.get_future().get()<< std::endl;
return 0;
}
上述代码就是在不同的线程之间去调用函数,涉及到了async promise,future等。这里关于异步任务的建立我们使用了async和thread两种方式,这两个方式各有千秋,具体区别如下:
在 C++ 中,std::thread 和 std::async 都可以用于创建并发任务,但它们之间有一些重要的区别:
调用方式:
std::thread 使用线程来执行指定的函数或可调用对象。
std::async 创建一个异步任务,通常将任务委托给线程池来执行。它返回一个 std::future 对象,允许您获取异步任务的结果。
返回值:
std::thread 不直接返回任务的结果。如果需要任务的结果,通常需要自行管理线程间通信(比如使用 std::mutex 或 std::condition_variable)。
std::async 返回一个 std::future 对象,可以用于获取异步任务的结果。
执行策略:
std::thread 允许您显式地创建一个新线程并执行指定的函数或可调用对象。
std::async 通常会根据执行策略(指定为 std::launch::async 或 std::launch::deferred)来决定是否创建新线程执行任务。
管理:
std::thread 提供了对线程的直接控制,如创建、加入、分离线程等。
std::async 通常将任务委托给线程池,隐藏了底层线程的管理。但是,具体的执行方式可能会因实现而异。
综上所述,std::thread 适合对线程进行精细控制,而 std::async 更适合用于异步任务的简单启动和结果获取