1、回调函数
回调函数定义:把函数的指针或者地址作为参数传递给另一个参数,当这个指针被用来调用其所指向的函数时,那么这就是一个回调的过程,这个被回调的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或者条件发生时由另外的一方调用的,用于对该事件或者条件进行响应,是在两个独立函数或者独立类通信的通道。
回调机制原理如下:
- 调用者不知道具体事件发生时需要调用的具体函数
- 被调函数不知道何时被调用,只知道需要完成的任务
- 当具体事件发生时,调用者通过函数指针来调用具体函数
- 回调机制中的调用者和被调函数互不依赖。
2、回调函数和普通函数的区别
普通函数的调用过程如下:
1)主程序运行,遇到普通函数的调用后,进入被调用函数体内执行内容。
2)等待被调用函数执行完毕后,主程序继续往下执行。
3)从主程序的角度看,这个过程为“调用–>等待被调用函数执行完毕–>继续执行”。
回调函数的调用过程如下:
1)主程序运行,遇到回调函数的调用后,发起调用;
2)主程序不等回调函数执行完毕,而是立即返回并继续往下执行。
3)调用程序执和被调用函数同时在执行。当被调函数执行完毕后,被调函数会反过来调用某个事先指定函数,以通知主程序;
这个过程称为回调(Callback),这正是回调函数名称的由来。
3、同步回调和异步回调
回调函数分为同步回调和异步回调。同步回调可以是单线程也可以是多线程,如果多线程同步回调的话,主线程需要等待子线程回调完成后再继续执行。而异步回调必须是多线程或多进程(每个进程可以是单线程),异步回调必须依靠多线程或多进程才能完成。
(1) 同步回调:把函数b传递给函数a。执行a的时候,回调了b,a要一直等到b执行完才能继续执行;
(2) 异步回调:把函数b传递给函数。执行a的时候,回调了b,然后a就继续往后执行,b独自执行。
4、异步回调示例
4.1 示例说明
下面我们来编写一个工作中常见的异步回调函数使用案例。案例说明:主线程发起任务,创建子线程来进行任务处理,同时主线程通过回调函数来检测任务进度状态,以便确认任务是否执行完成。
案例详情如图所示:
1)主线程传入任务参数;
2)创建子线程并传入参数;
3)主线程结束;
4)子线程启动开始工作;
5)通过回调函数监测任务执行状态。
4.2 示例源码
main.cpp
// main.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "workThread.h"
#include <iostream>
using namespace std;
// 回调函数监测状态
bool OnStatusChangeCallBack(workParam& stuParam)
{
if (stuParam.nStatus == -1)
{
printf("Status: waiting! sInput: %s, sOutput: %s! \n", stuParam.sInput.c_str(), stuParam.sOutput.c_str());
}
else if(stuParam.nStatus == 0)
{
printf("Status: begin! sInput: %s, sOutput: %s! \n", stuParam.sInput.c_str(), stuParam.sOutput.c_str());
}
else if (stuParam.nStatus == 1)
{
printf("Status: midding! sInput: %s, sOutput: %s! \n", stuParam.sInput.c_str(), stuParam.sOutput.c_str());
}
else if (stuParam.nStatus == 2)
{
printf("Status: finish! sInput: %s, sOutput: %s! \n", stuParam.sInput.c_str(), stuParam.sOutput.c_str());
}
return true;
}
int main(int argc, char* args[])
{
printf("The mainThread input workParam! \n");
// 设定参数
workParam stuParam;
stuParam.nStatus = -1;
stuParam.sInput = "myWorkdir";
stuParam.sOutput = "xxx";
printf("The mainThread create childThread! \n");
// 创建子线程
workThread myWork;
myWork.setParam(stuParam);
myWork.GetCallbackData(OnStatusChangeCallBack);
printf("The mainThread is over! \n\n");
return 0;
}
workThread.h
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <thread>
struct workParam
{
workParam()
{
nStatus = -1;
sInput = "";
sOutput = "";
}
workParam& operator = (const workParam& src)
{
nStatus = src.nStatus;
sInput = src.sInput;
sOutput = src.sOutput;
return* this;
}
int nStatus;
std::string sInput;
std::string sOutput;
};
typedef bool(*CallFun)(workParam& stuParam);
class workThread
{
public:
workThread();
~workThread();
void Realese();
void setParam(const workParam& stuParam);
void Run();
CallFun m_callFun;
void GetCallbackData(CallFun call_fun);
private:
std::thread m_workThread;
workParam m_stuParam;
};
workThread.cpp
#include "workThread.h"
#include <functional>
#include <windows.h>
using namespace std;
workThread::workThread()
{
}
workThread::~workThread()
{
Realese();
}
void workThread::Realese()
{
m_workThread = std::thread(std::bind(&workThread::Run, this));
if (m_workThread.joinable())
{
m_workThread.join();
}
}
void workThread::setParam(const workParam& stuParam)
{
m_stuParam = stuParam;
}
void workThread::Run()
{
printf("The childThread is working! \n");
{
m_stuParam.nStatus = 0;
m_callFun(m_stuParam);
}
for (int i = 0; i < 100; i++)
{
if (i == 50)
{
Sleep(1000);
{
m_stuParam.nStatus = 1;
m_stuParam.sOutput = "D";
m_callFun(m_stuParam);
}
}
if (i == 99)
{
Sleep(1000);
{
m_stuParam.nStatus = 2;
m_stuParam.sOutput = "FF";
m_callFun(m_stuParam);
}
}
}
}
void workThread::GetCallbackData(CallFun call_fun)
{
m_callFun = call_fun;
}