当前位置: 首页 > news >正文

“深入浅出”系列之C++:(4)回调函数

在写项目的时候遇见一个问题,现在的需求是主项目需要拿到子项目的结果来进行显示,那么如何集成呢,子项目里面有一个MainWindow类,类里

回调函数是一种通过函数指针将函数作为参数传递给另一个函数的编程技术。这种机制允许程序在特定事件发生时(如异步操作完成、用户输入等)调用预定义的函数。

在 C++ 中,回调函数通常与函数指针、模板、std::function 和 std::bind 等特性一起使用。以下是一些关于在 C++ 中使用回调函数的示例和说明:

  1. 函数指针作为回调函数
    函数指针是最基本的回调机制。你可以定义一个函数指针类型,并将其作为参数传递给另一个函数。当特定条件满足时,该函数会调用通过指针传递的函数。

typedef void (*CallbackFunctionType)(int);void myCallbackFunction(int result) {std::cout << "Callback called with result: " << result << std::endl;
}void doSomethingAsync(CallbackFunctionType callback, int value) {// 模拟异步操作std::this_thread::sleep_for(std::chrono::seconds(1));callback(value); // 调用回调函数
}int main() {doSomethingAsync(myCallbackFunction, 42);return 0;
}

2.使用 std::function 和 std::bind
C++11 引入了 std::function 和 std::bind,它们提供了更灵活和强大的回调机制。std::function 是一个通用、多态的函数封装器,它可以存储、调用或复制任何可以调用的目标,包括普通函数、Lambda 表达式、函数对象以及成员函数指针。

#include <functional>
#include <thread>
#include <chrono>
#include <iostream>void myCallbackFunction(int result) {std::cout << "Callback called with result: " << result << std::endl;
}void doSomethingAsync(std::function<void(int)> callback, int value) {// 模拟异步操作std::this_thread::sleep_for(std::chrono::seconds(1));callback(value); // 调用回调函数
}int main() {auto lambdaCallback = [](int result) {std::cout << "Lambda callback called with result: " << result << std::endl;};doSomethingAsync(myCallbackFunction, 42);doSomethingAsync(lambdaCallback, 84);// 使用 std::bind 绑定成员函数回调class MyClass {public:void memberCallback(int result) {std::cout << "Member callback called with result: " << result << std::endl;}};MyClass obj;doSomethingAsync(std::bind(&MyClass::memberCallback, &obj, std::placeholders::_1), 126);return 0;
}

在这个例子中,doSomethingAsync 函数接受一个 std::function<void(int)> 类型的回调函数参数,这使得它可以接受任何符合该签名的可调用对象,包括普通函数、Lambda 表达式和通过 std::bind 绑定的成员函数。

使用回调函数可以使代码更加模块化和可重用,因为它允许你将特定的行为(回调)与触发该行为的事件(如异步操作的完成)分离开来。这在处理异步编程、事件驱动编程或需要高度灵活性和可扩展性的应用程序中特别有用。

异步回调是一种常见的编程技术,特别是在处理耗时操作或需要等待外部资源返回结果的情况下,异步回调可以提高程序的效率和响应速度。以下是对异步回调的详细介绍以及如何在异步回调中加锁的方法:

一、定义:

异步回调是指调用者发起一个请求后,不需要立即等待结果返回,而是继续执行其他操作。当结果返回时,调用者通过回调函数来获取结果,从而完成整个操作。

优点:

提高程序的并发性和响应速度,避免主线程阻塞。

充分利用系统资源,提高程序效率。

降低系统复杂度,通过回调函数将异步操作的结果传递给调用者,降低了程序的耦合度。

应用场景:广泛应用于网络请求、文件读写、数据库查询等需要等待结果返回的操作。在Android开发中,异步回调常用于处理UI更新、网络请求等耗时操作,保证程序的流畅性和用户体验。

二、异步回调中的加锁机制

在异步回调中,由于存在多个线程同时访问共享资源的情况,因此需要加锁机制来保护共享资源,避免数据竞争或并发访问的问题。

使用互斥锁(Mutex):

在对象的生命周期管理中引入互斥锁,确保在回调函数执行时,对象不会被销毁。

示例代码(以C++为例):

class MyClass {
public:MyClass() : value(0) {}void doAsyncTask(std::function<void(int)> callback) {std::lock_guard<std::mutex> lock(mtx); // 加锁
callbackFunction = callback;
// 模拟异步任务
std::thread([this]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::lock_guard<std::mutex> lock(mtx); // 回调时再加锁
if (callbackFunction) {
callbackFunction(value);
}
}).detach();
}
void setValue(int v) {
std::lock_guard<std::mutex> lock(mtx); // 加锁
value = v;
}
~MyClass() {
std::lock_guard<std::mutex> lock(mtx); // 在析构函数中加锁,避免回调访问已销毁的对象
callbackFunction = nullptr;
}
private:
int value;
std::function<void(int)> callbackFunction;
std::mutex mtx;
};

使用智能指针:

使用std::shared_ptr来管理对象的生命周期,可以避免野指针问题。

回调函数中使用智能指针,确保对象在回调期间不会被销毁。

示例代码(以C++为例):

class MyClass : public std::enable_shared_from_this<MyClass> {public:void doAsyncTask() {auto self = shared_from_this();std::thread([self]() {std::this_thread::sleep_for(std::chrono::seconds(1));self->callback(); // 通过智能指针调用回调,确保对象存在}).detach();}void callback() {std::cout << "Async task result" << std::endl;}};

四、注意事项

避免死锁:在使用加锁机制时,要注意避免死锁的发生。例如,确保在持有锁的情况下不会调用可能导致再次持有相同锁的代码。

性能考虑:加锁机制会增加一定的性能开销,因此在设计系统时要权衡加锁带来的性能和安全性之间的平衡。

代码可读性:在编写加锁代码时,要注意代码的可读性和可维护性。例如,可以使用明确的变量名和注释来描述锁的作用和范围。

综上所述,异步回调是一种重要的编程技术,在使用加锁机制时,要注意避免死锁、考虑性能开销以及保持代码的可读性和可维护性

线程池的任务队列为什么要用到回调函数,上锁的目的是什么

线程池的任务队列使用回调函数以及上锁的目的,都是为了更好地管理和协调线程池中的任务执行,确保线程安全和提高程序的并发性能。

回调函数的作用

在线程池中,回调函数通常用于处理任务执行完成后的结果或进行后续操作。当线程池中的一个线程完成了一个任务的执行后,它可以调用一个回调函数来处理该任务的结果。这种机制允许程序在任务完成时不阻塞主线程或执行线程,而是异步地处理结果。使用回调函数的好处包括:

  1. 解耦合:回调函数将任务的生产和消费分离开来,使得代码更加模块化和易于维护。
  2. 异步处理:回调函数允许程序在等待任务完成时继续执行其他操作,提高了程序的并发性能。
  3. 灵活性:回调函数可以定义不同的处理逻辑,以适应不同的任务类型和结果处理方式。

上锁的目的

线程池中的锁机制是为了确保多个线程并发访问时的安全性。在多线程环境下,线程池的状态、任务队列和工作线程都是共享资源,需要同步访问以防止数据竞争和不一致性。上锁的目的包括:

  1. 保护线程池的全局状态:全局锁(如Main Lock)用于保护线程池的状态,如任务队列的当前大小、线程池的运行状态等。这防止了多个线程同时修改这些状态,导致数据不一致或竞争条件。
  2. 保护任务队列:任务队列本身也是共享资源,需要锁来保护。这防止了多个线程同时向任务队列中插入或取出任务,从而保证了任务队列的一致性。
  3. 保护单个工作线程的任务执行:每个工作线程(Worker)也可以有自己的锁,用于保证线程执行任务时的同步。这防止了多个线程对同一个任务执行的竞争问题,确保了单个工作线程的任务执行是安全的。

总的来说,线程池中的回调函数和上锁机制都是为了更好地管理和协调线程池中的任务执行,确保线程安全和提高程序的并发性能。这些机制使得线程池在多线程编程中成为了一个强大而灵活的工具。

回调函数的原理主要基于事件驱动的编程模型,以及函数作为一等公民(First-Class Function)的特性。以下是对回调函数原理的详细解释:

一、定义与概念

回调函数(Callback Function)是一种编程模式,它允许一个函数作为参数传递给另一个函数,并在某个特定事件或条件发生时被调用。这种机制使得程序能够在不阻塞主线程的情况下,异步地处理任务或事件。

二、工作原理

  1. 函数作为参数传递

    • 在一个函数中,你可以定义一个或多个参数,这些参数可以是任何数据类型,包括函数类型。
    • 当另一个函数需要执行某个操作时,它可以将这个操作封装成一个函数,并通过参数将这个函数传递给第一个函数。
  2. 事件或条件触发

    • 在某个特定的事件或条件发生时(如异步任务完成、用户输入等),第一个函数会调用通过参数传递进来的函数(即回调函数)。
    • 回调函数在此时被触发,执行其封装的操作。
  3. 异步执行

    • 回调函数通常用于异步编程中,允许程序在等待某个事件完成时继续执行其他操作。
    • 当事件完成时,回调函数会被自动调用,处理结果或进行后续操作。

三、实现方式

  1. 函数指针

    • 在C和C++等语言中,回调函数通常通过函数指针来实现。
    • 函数指针是一个指向函数的指针,它允许你将一个函数作为参数传递给另一个函数。
  2. 高阶函数

    • 在Python、JavaScript等语言中,高阶函数允许你将一个函数作为参数传递给另一个函数,并返回一个新的函数。
    • 这些语言中的匿名函数(Lambda函数)和闭包(Closure)也为回调函数的实现提供了便利。
  3. 事件监听与回调

    • 在事件驱动的编程模型中,如JavaScript的DOM事件处理、GUI框架中的事件监听等,回调函数常用于处理用户交互、按钮点击等事件。

四、应用场景

  1. 异步任务处理

    • 如文件下载、网络请求等异步操作,可以使用回调函数来处理任务完成后的结果。
  2. 事件处理

    • 在事件驱动的系统中,如GUI应用、Web应用等,回调函数常用于处理用户交互事件(如按钮点击、鼠标移动等)。
  3. 函数式编程

    • 在函数式编程中,回调函数是高阶函数的核心,可以用于处理数据流、过滤器和迭代器等操作。
  4. 异常处理与监控

    • 回调函数也可以用于处理异常或执行监控任务,当某个操作失败或出现异常时,可以调用回调函数来处理错误或进行恢复操作。

五、优缺点

优点

  1. 提高灵活性:可以根据需求指定不同的回调逻辑,提升函数的通用性。
  2. 模块化:将复杂的逻辑封装成独立的函数,使得主程序的代码更加简洁和易于维护。
  3. 异步编程:支持异步操作,不阻塞主线程,提高程序的并发性能。

缺点

  1. 可读性:多层嵌套的回调函数可能难以阅读和维护。
  2. 调试复杂度:异步回调的执行顺序难以预测,可能增加调试的复杂度。
  3. 代码依赖性:回调函数需要直接传递给主函数,可能导致代码之间的依赖性增强。

综上所述,回调函数是一种强大而灵活的编程技术,在异步编程、事件处理和函数式编程等领域有着广泛的应用。然而,在使用回调函数时,也需要注意其可能带来的可读性和调试复杂度等问题。

相关文章:

“深入浅出”系列之C++:(4)回调函数

在写项目的时候遇见一个问题&#xff0c;现在的需求是主项目需要拿到子项目的结果来进行显示&#xff0c;那么如何集成呢&#xff0c;子项目里面有一个MainWindow类&#xff0c;类里 回调函数是一种通过函数指针将函数作为参数传递给另一个函数的编程技术。这种机制允许程序在特…...

Mysql--运维篇--主从复制和集群(主从复制I/O线程,SQL线程,二进制日志,中继日志,集群NDB)

一、主从复制 MySQL的主从复制&#xff08;Master-Slave Replication&#xff09;是一种数据冗余和高可用性的解决方案&#xff0c;它通过将一个或多个从服务器&#xff08;Slave&#xff09;与主服务器&#xff08;Master&#xff09;同步来实现。主从复制的基本原理是&#…...

设计模式 行为型 状态模式(State Pattern)与 常见技术框架应用 解析

状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为&#xff0c;使得对象看起来好像修改了它的类。这种设计模式的核心思想是将对象的状态和行为封装成不同的状态类&#xff0c;通过状态对象的行为改变来避免…...

计算机网络 (38)TCP的拥塞控制

前言 TCP拥塞控制是传输控制协议&#xff08;Transmission Control Protocol&#xff0c;TCP&#xff09;避免网络拥塞的算法&#xff0c;是互联网上主要的一个拥塞控制措施。 一、目的 TCP拥塞控制的主要目的是防止过多的数据注入到网络中&#xff0c;使网络能够承受现有的网络…...

鸿蒙面试 2025-01-09

鸿蒙分布式理念&#xff1f;&#xff08;个人认为理解就好&#xff09; 鸿蒙操作系统的分布式理念主要体现在其独特的“流转”能力和相关的分布式操作上。在鸿蒙系统中&#xff0c;“流转”是指涉多端的分布式操作&#xff0c;它打破了设备之间的界限&#xff0c;实现了多设备…...

【关于for循环的几种写法】

关于for循环的几种写法 在 C 中&#xff0c;for(int i 0; i < n; i) 是一种常见的循环写法&#xff0c;用于遍历从 0 到 n-1 的索引。如果你希望简化这种写法&#xff0c;可以使用以下几种方法&#xff1a; 1. 使用范围 for 循环 如果你需要遍历一个容器&#xff08;如数…...

Apache和PHP:构建动态网站的黄金组合

在当今的互联网世界&#xff0c;网站已经成为了企业、个人和机构展示自己、与用户互动的重要平台。而在这些动态网站的背后&#xff0c;Apache和PHP无疑是最受开发者青睐的技术组合之一。这一组合提供了高效、灵活且可扩展的解决方案&#xff0c;帮助您快速搭建出强大的网站&am…...

免费开源的下载工具Xdown

软件介绍 Xdown是一款功能强大的开源免费下载工具&#xff0c;专为PC端用户设计&#xff0c;支持多种协议和下载方式。 1、多线程下载 Xdown支持最高128线程的并发下载&#xff0c;能够将文件分割成多个部分同时下载&#xff0c;从而显著提升下载速度。 2、多种协议支持 该…...

Three.js 数学工具:构建精确3D世界的基石

文章目录 前言一、向量&#xff08;Vectors&#xff09;二、矩阵&#xff08;Matrices&#xff09;三、四元数&#xff08;Quaternions&#xff09;四、欧拉角&#xff08;Euler Angles&#xff09;五、颜色&#xff08;Colors&#xff09;六、几何体生成器&#xff08;Geometr…...

如何明智地提问

如何明智地提问的重要总结&#xff0c;让我为主要观点添加一些具体的实践建议&#xff1a; 提问前的准备工作 尝试在 Google、Stack Overflow 等平台搜索相似问题阅读相关文档和错误日志尝试自己调试和排查问题记录下已尝试过的解决方案 选择合适的提问平台 Stack Overflow…...

Microsoft Sql Server 2019 函数理解

说到函数&#xff0c;首先和存储过程作个比较吧&#xff0c;两者有一个共同点都是预编译优化后存储在磁盘中&#xff0c;所以效率 要比T-SQL高一点点。值得注意的是&#xff0c;存储过程可以创建或访问临时表&#xff0c;而函数不可以&#xff1b; 同时函数不可 以修改表中的数…...

自定义日期转换配置

文章目录 1.日期问题出现原因以及解决方案概述1.图示2.三种解决方案概述1.对于表单数据 application/x-www-form-urlencoded2.对于JSON数据1.使用JsonFormat注解2.自定义Jackson日期转换配置 2.解决方案common-web-starter1.目录2.BaseController.java 使用InitBinder解决表单数…...

“AI智能服务平台系统,让生活更便捷、更智能

大家好&#xff0c;我是资深产品经理老王&#xff0c;今天咱们来聊聊一个让生活变得越来越方便的高科技产品——AI智能服务平台系统。这个系统可是现代服务业的一颗璀璨明珠&#xff0c;它究竟有哪些魅力呢&#xff1f;下面我就跟大家伙儿闲聊一下。 一、什么是AI智能服务平台系…...

SQL美化器优化

文章目录 1.目录2.代码 1.目录 2.代码 package com.sunxiansheng.mybatis.plus.inteceptor;import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.*; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.*…...

我的128天创作之路:回顾与展望

大家好呀&#xff01;今天来和你们分享一下我的创作历程&#x1f601;。 一、机缘 最开始创作呢&#xff0c;是因为在学习 C 的 STL 时&#xff0c;像 string、list、vector 这些模板可把我折腾得够呛&#xff0c;但也让我学到了超多东西&#xff01;我就想&#xff0c;要是把我…...

内核配置参数整理

#参考网页 linux5.2 &#xff1c;.config&#xff1e;文件注释 详细解释 CONFIG_ARMy&#xff1a;启用ARM架构支持&#xff0c;这是ARM处理器专用的内核配置选项。 CONFIG_ARM_HAS_SG_CHAINy&#xff1a;启用对散列表&#xff08;scatter-gather&#xff09;链的支持&#xf…...

SpringBoot整合Easy-es

一.什么是Easy-Es Easy-Es&#xff08;简称EE&#xff09;是一款基于ElasticSearch(简称Es)官方提供的RestHighLevelClient打造的ORM开发框架&#xff0c;在 RestHighLevelClient 的基础上,只做增强不做改变&#xff0c;为简化开发、提高效率而生,您如果有用过Mybatis-Plus(简称…...

于交错的路径间:分支结构与逻辑判断的思维协奏

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。* 这一节内容很多&#xff0c;文章字数达到了史无前例的一万一&#xff0c;我们要来学习分支与循环结构中…...

Linux之读者写者模型与特殊锁的学习

目录 读者写者模型 特殊锁 悲观锁 自旋锁 在前几期&#xff0c;我们学习了多线程的生产者和消费者模型&#xff0c;生产者和消费者模型中&#xff0c;有三种关系&#xff0c;两个角色&#xff0c;一个场所&#xff0c;那么读者写者模型和生产者消费者模型有什么关联吗&…...

回溯专题 记录

回溯的题目按照这套模板进行&#xff1b; 我感觉整体逻辑还是递归&#xff0c;只不过有了pop_back才是回溯概念&#xff1b; class Solution {public:vector<int> path;vector<vector<int>> ans;void backtracking(int n,int k,int startindex){if(path.…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法

树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作&#xff0c;无需更改相机配置。但是&#xff0c;一…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...