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;
}