Sylar C++高性能服务器学习记录06 【线程模块-代码分析篇】

早在19年5月就在某站上看到sylar的视频了,一直认为这是一个非常不错的视频,还有幸加了sylar本人的wx,由于本人一直是自学编程,基础不扎实,也没有任何人的督促,没能坚持下去,每每想起倍感惋惜。恰逢互联网寒冬,在家无事,遂提笔再续前缘。

为了能更好的看懂sylar,本套笔记会分两步走,每个系统都会分为两篇博客。
分别是【知识储备篇】和【代码分析篇】
(ps:纯粹做笔记的形式给自己记录下,欢迎大家评论,不足之处请多多赐教)


线程模块-代码分析

0.std::function & swap方法

有很多时候,很多知识是百度或者google都获取不到的,因为知识是变通的,网上搜索到的更多的是教学的基本知识。而往往你想要的是,工作经验积累后的知识。就好比你在大学学了C++,依旧看不懂Sylar一样。因为知识的运用绝对不是:学会基础后就能直接融会贯通的。这里我会举以下一个栗子🌰


0.1 如何交换两个方法:

#include <iostream>
#include <functional>

void func0(){
    std::cout << "This is func0 " << std::endl;
}

void func1(){
    std::cout << "This is func1" << std::endl;
}


int main(int argc, char** argv){
    std::function<void(void)> fa = func0;
    std::function<void(void)> fb = func1;

    std::cout << "====交换前====" << std::endl;
    fa();
    std::cout << "fa address: " << &fa << std::endl;
    fb();
    std::cout << "fb address: " << &fb << std::endl;

    //开始交换
    fa.swap(fb);

    std::cout << "====交换后====" << std::endl;
    fa();
    std::cout << "fa address: " << &fa << std::endl;
    fb();
    std::cout << "fb address: " << &fb << std::endl;

    return 0;
}

可以看到以下输出:

====交换前====
This is func0
fa address: 0x7fff20f52d90
This is func1
fb address: 0x7fff20f52db0
====交换后====
This is func1
fa address: 0x7fff20f52d90
This is func0
fb address: 0x7fff20f52db0

可以看到两个方法确实被交换了,但是方法的地址没有被交换。
很好理解,甚至我不需要解释,还是解释一下吧:

无论是方法还是属性,最终都将是10101010110这一类的二进制,没有任何区别,
那为什么计算机知道,哪些是数据,哪些是指令呢?
因为放在不同的区域,数据区的就是数据,代码区的就是指令。
一般来说我们交换两块内存中的内容指的是交换数据,也就是将两块处于数据段中的内容交换。
但是现在是将两块代码段中的内容进行交换,也就是计算机将代码段的指令当做是数据进行交换处理。
随后在调用方法时再次将交换后的内容当回代码段中的指令进行执行。
所以方法的地址没有改变,而指令却互换了。

如果还不能理解那我就换个说法,如下
如何理解属性和方法:

炒饭可以当名词,也可以当动词
菜单上写着【炒饭】,意思是这家店能提供【炒饭】这个食物。
菜谱上写着【炒饭】,意思是能教你【炒饭】的方法。
所以在不同地方【炒饭】指代的意思不同。

如何理解交换数据(属性):

有两个盘子:A盘子放的是炒饭,B盘子放的是炒面
交换数据后:A盘子放炒面,B盘子放炒饭   
所以盘子没变,东西变了。

如何理解交换方法:

有两个文件夹:A文件夹中放着【炒饭的菜谱】,B文件夹中放着【炒面的菜谱】
交换方法(交换菜谱后):A文件夹中放【炒面的菜谱】,B文件夹中放【炒饭的菜谱】
所以文件夹没变,菜谱变了。

好!当你不再困惑以上交换相关的知识之后,我们继续往下讲。


0.2 交换函数的巧妙思维与注意点
不是一定要AB两个文件夹都有菜谱才能交换。
我们可以用一个空文件夹和一个装有【炒饭菜谱】的文件夹进行内容交换。
有两个文件夹:A文件夹有【炒饭菜谱】,B文件夹是空的
交换后:A文件夹被清空了,B文件夹中装入了【炒饭菜谱】
可以看到我们间接的清空了A文件夹,所以swap函数除了能交换,其实也隐藏了转移的效果。
具体代码如下:

#include <iostream>
#include <functional>

//【炒饭】菜谱(这里方法定义的时候叫菜谱,调用的时候叫根据菜谱炒饭)
void func0(){
    std::cout << "炒饭中..." << std::endl;
}

int main(int argc, char** argv){
	//A文件夹装有【炒饭】菜谱
    std::function<void(void)> fa = func0;
    //B文件夹空着
    std::function<void(void)> fb;
    //将两个文件夹内容互换,也就是将A文件夹的炒饭菜谱转移到B文件夹中
	fb.swap(fa);
	//根据B文件夹中的【炒饭】菜谱,开始炒饭操作
	std::cout << "根据B文件夹中的【炒饭】菜谱,开始炒饭操作" << std::endl;
	fb();
	//此处因为上面的交换操作,导致A文件夹已经空了
	//!!!此处的调用好比是找不到菜谱,所以会报错!!!
	std::cout << "根据A文件夹中的【炒饭】菜谱,开始炒饭操作" << std::endl;
	fa();
    return 0;
}

以下是输出结果(可以看到报错了,因为找不到菜谱了):

根据B文件夹中的【炒饭】菜谱,开始炒饭操作
炒饭中...
根据A文件夹中的【炒饭】菜谱,开始炒饭操作
terminate called after throwing an instance of 'std::bad_function_call'
  what():  bad_function_call
Aborted

所以我们可以得出结论:
1.swap方法可以做交换操作。
2.swap方法可以做转移操作。
3.swap方法转移后,不可以再调用原有函数。


0.3 将函数作为参数传递
这里只需要对上面的方法进行改造就行:

#include <iostream>
#include <functional>

void print(){
    std::cout << "Hello I am XYZ" << std::endl;
}

void func(std::function<void()> cb){
    std::cout << "函数传入后的地址:" << &cb << std::endl;
    std::cout << "传入函数后调用" << std::endl;
    cb();
}


int main(int argc, char** argv){
    std::function<void()> f = print;
    std::cout << "函数传入前的地址:" << &f << std::endl;
    func(f);
    std::cout << "回调完成后再次调用" << std::endl;
    f();
    return 0;
}

可以看到以下输出:

函数传入前的地址:0x7ffd12c6adb0
函数传入后的地址:0x7ffd12c6aaa0
传入函数后调用
Hello I am XYZ
回调完成后再次调用
Hello I am XYZ

可以看到调用非常成功!但是传入前后地址不一样,证明是值传递,会有复制的操作,不是很理想。
所以我们将值传递改为引用传递,避免不必要的拷贝操作。


0.4 将函数的引用作为参数传递

#include <iostream>
#include <functional>

void print(){
    std::cout << "Hello I am XYZ" << std::endl;
}

//!!!只要改动此处入参类型就行 加了& !!!
void func(std::function<void()>& cb){
    std::cout << "函数传入后的地址:" << &cb << std::endl;
    std::cout << "传入函数后调用" << std::endl;
    cb();
}


int main(int argc, char** argv){
    std::function<void()> f = print;
    std::cout << "函数传入前的地址:" << &f << std::endl;
    func(f);
    std::cout << "回调完成后再次调用" << std::endl;
    f();
    return 0;
}

可以看到以下输出中,地址是同一个(避免了不必要的拷贝操作):

函数传入前的地址:0x7ffee22f8170
函数传入后的地址:0x7ffee22f8170
传入函数后调用
Hello I am XYZ
回调完成后再次调用
Hello I am XYZ

以上看似比较完美的方式,仔细想想可能还有问题。
有一些情况下,我们作为入参的函数被传入后希望不再其他地方被使用,
或者这个函数除了作为入参在内部调用之外没有其他地方被调用。
那么我们希望再该函数作为入参调用后将对其的引用解除掉。

翻译成人话就是:有个方法fun1使用引用传递的方式,作为入参传递到fun2内部,
在fun2内部调用传入的fun1后,将fun1方法置空。
所以我们看到**【置空】就能想到上面的使用swap进行【转移】**。
代码如下:

#include <iostream>
#include <functional>

void print(){
    std::cout << "Hello I am XYZ" << std::endl;
}

void func(std::function<void()>& cb){
    std::cout << "函数传入后的地址:" << &cb << std::endl;
    std::cout << "传入函数后调用" << std::endl;
    //这里用局部变量来接收,这样超出作用域后会自动销毁
    std::function<void()> call_back;
    //!!!此处进行了转移操作!!!
    call_back.swap(cb);
    call_back();
}

int main(int argc, char** argv){
    std::function<void()> f = print;
    std::cout << "函数传入前的地址:" << &f << std::endl;
    func(f);
    std::cout << "回调完成后再次调用" << std::endl;
    //!!!这里再次调用一定会出错!!!
    f();
    return 0;
}

可以看到输出如下:

函数传入前的地址:0x7ffc81e0dcb0
函数传入后的地址:0x7ffc81e0dcb0
传入函数后调用
Hello I am XYZ
回调完成后再次调用
terminate called after throwing an instance of 'std::bad_function_call'
  what():  bad_function_call
Aborted

可以看到上面最后的报错!这正是我们想要的结果!
通过报错提示我们不要在回调函数外部再次调用这个方法。
这个方式其实可以有效避免一些智能指针引用计数问题,具体的会在之后篇章里介绍 【记得关注我】


实在太累了,连续熬夜好几天了,今天先到这吧,明天我来继续补充。
关注我的小伙伴可以明天晚上再来这里看后续哈。
今天是【2024-04-25 23:05】
后面部分可以明天再来看哦,因为我还没刷完这部分视频
我先不写了,继续刷sylar视频去了。。。

未完待续…


1. 使用pthread库

在正式学习Sylar线程模块之前我们先熟悉以下C++11的pthread库的基本使用。
不知道在做的各位是否有遇到问题,总之我是一步一磕绊
我是拉取Sylar项目后学多少就切到对应分支查看对应代码的,所以会遇到这个问题。
我查看了Sylar项目的CMakeLists.txt,应该是有问题的,这里给个截图给大家避坑了(对应版本:git reset --hard 846054e):
在这里插入图片描述
以下是我修改后的CMakeLists.txt,因为我在这一章只学线程相关的,所以删除了一些不必要的东西:

cmake_minimum_required(VERSION 2.8)
project(sylar)

include (cmake/utils.cmake)

set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -ggdb -std=c++11 -Wall -Wno-deprecated -Werror -Wno-unused-function -Wno-builtin-macro-redefined")

include_directories(.)
include_directories(/apps/sylar/include)
link_directories(/apps/sylar/lib)

set(
	LIB_SRC
    sylar/log.cc
    sylar/util.cc
    sylar/config.cc
	)

add_library(sylar SHARED ${LIB_SRC})
force_redefine_file_macro_for_sources(sylar) #__FILE__

find_library(PTHREAD pthread)

set(
	LIBS
    sylar
    ${PTHREAD }
	)

add_executable(test_thread tests/test_thread.cc)
add_dependencies(test_thread sylar)
force_redefine_file_macro_for_sources(test_thread) #__FILE__
target_link_libraries(test_thread ${LIBS})

SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

我们先在test_thread.cc中编写以下代码,
这里我用截图是为了让大家自己敲一边,因为我自己写的时候才会遇到各种问题,
我自己出错和担心大家出错的地方做了标注:
在这里插入图片描述
如果你敲了以上的代码并且能输出以下信息:

====thread====
Hello thread 139660689327872
====end====

那么恭喜你,你已经会最基本的C++ pthread库的使用了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/574870.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习day3

一、距离度量 1.欧氏距离 2.曼哈顿距离 3.切比雪夫距离 4.闵可夫斯基距离 二、特征与处理 1.数据归一化 数据归一化是一种将数据按比例缩放&#xff0c;使之落入一个小的特定区间的过程。 代码实战 运行结果 2.数据标准化 数据标准化是将数据按照其均值和标准差进行缩放的过…

2024最新版JavaScript逆向爬虫教程-------基础篇之面向对象

目录 一、概念二、对象的创建和操作2.1 JavaScript创建对象的方式2.2 对象属性操作的控制2.3 理解JavaScript创建对象2.3.1 工厂模式2.3.2 构造函数2.3.3 原型构造函数 三、继承3.1 通过原型链实现继承3.2 借用构造函数实现继承3.3 寄生组合式继承3.3.1 对象的原型式继承3.3.2 …

paddle ocr v4 微调训练文字识别模型实践

识别步骤参考&#xff1a;https://github.com/PaddlePaddle/PaddleOCR/blob/main/doc/doc_ch/recognition.md 微调步骤参考:https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7.1/doc/doc_ch/finetune.md 训练必要性 原始模型标点符号和括号容易识别不到 数据准备…

【漏洞复现】Weblogic 任意文件上传漏洞(CVE-2018-2894)

漏洞简介 Oracle在7月更新中&#xff0c;修复了Weblogic Web Service Test Page中一处任意文件上传漏洞&#xff0c;Web Service Test Page在"生产模式"下默认不开启&#xff0c;所以该漏洞有一定限制&#xff0c;利用该漏洞&#xff0c;可以上传任意.jsp文件&#x…

用Redis实现获取验证码,外加安全策略

安全策略 一小时内只能获取三次&#xff0c;一天内只能获取五次 Redis存储结构 代码展示 import cn.hutool.core.util.RandomUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import org.spri…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-6

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

RK3588 - RKNN(Rockchip 神经处理单元)的逆向工程

本文翻译自https://jas-hacks.blogspot.com/2024/02/rk3588-reverse-engineering-rknn.html RK3588 NPU 的内部操作和功能主要隐藏在名为RKNPU2的闭源 SDK 中。由于对大型语言模型 (LLM) 的兴趣以及对transform模型最佳矩阵乘法的追求&#xff0c;想了解 RKNPU SDK 新引入的矩阵…

值得让英伟达CEO黄仁勋亲自给OpenAI配送的AI服务器!一文带你了解算力,GPU,CPU!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【C++】双指针算法:和为s的两个数字

1.题目 虽然在牛客上是个中等题&#xff0c;但我感觉是比较简单的。大家在看完这篇文章后可以看看我的上一篇文章&#xff1a;有效三角形的个数。本文章的题目的解法只是有效三角形的个数这道题目的一个环节。看懂这篇文章后可以更好的解决有效三角形个数那道题目&#xff01; …

电力系统IEC-104报文主要常用详解

文章目录 1️⃣ IEC-1041.1 前言1.2 报文分类1.3 U帧报文1.3.1 常见报文1.3.1 报文解析 1.4 S帧报文1.4.1 说明1.4.2 报文解析 1.5 I帧报文1.5.1 报文解析 1.6 控制域I帧报文S帧报文U帧报文介绍 1.7 ASDU1.7.1 常见类型标识1.7.2 常见结构限定词1.7.3 常见传送原因1.7.4 信息体…

艾瑞泽5汽车电子控制单元CAN通信数据读写车辆网络系统交互接口

艾瑞泽5的网关接口数据交换通常涉及车辆内部电子设备之间的信息传输&#xff0c;包括车身系统、娱乐系统、远程控制、车辆状态监控、CAN数据采集分析、整车DBC控制策略等信息。 艾瑞泽5作为一款采用CAN协议的汽车&#xff0c;其CAN通信的开发可以提高车辆的安全性、可靠性和实…

Visual Studio Code使用

目录 1.python的调试 2.c的运行 方法1&#xff1a; 方法2&#xff1a; 3.c的调试 3.1调试方法一&#xff1a;先生成执行文件&#xff0c;再调试 3.2调试方法二&#xff1a;同时生成执行文件&#xff0c;调试 4.tasks.json 与launch.json文件的参考 4.1C生成执行文件tas…

uniapp H5实现签名

第一种&#xff1a;跳转签名页面 1、创建审核页面audit.vue <template><view><uni-section title""><view class"auditClass"><uni-forms :model"baseFormData" ref"baseFormRef" :rules"rules&quo…

数据结构初阶——树和二叉树

数据结构初阶——树和二叉树 1. 树的概念和结构1.1 树的概念1.2 树的表示 2. 二叉树2.1 二叉树的概念和结构2.2 二叉树的存储结构2.2.1 顺序存储2.2.2 链式存储 3. 二叉树的顺序结构及实现——堆3.1 堆的概念和结构3.2 堆的实现3.2.1 堆的定义3.2.2 堆的向上调整3.2.3 堆的向下…

【网络安全】安全事件管理处置 — 事件分级分类

专栏文章索引&#xff1a;网络安全 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 目录 一、安全事件分级 二、应急事件分级 三、安全事件分类 四、常见安全事件原因分析 1.web入侵 2.漏洞攻击 3.网络攻击 一、安全事件分级 在对安全事件的应急响应过程中&#xf…

【Hadoop】-Apache Hive概述 Hive架构[11]

目录 Apache Hive概述 一、分布式SQL计算-Hive 二、为什么使用Hive Hive架构 一、Hive组件 Apache Hive概述 Apache Hive是一个在Hadoop上构建的数据仓库基础设施&#xff0c;它提供了一个SQL-Like查询语言来分析和查询大规模的数据集。Hive将结构化查询语言&#xff08;…

LT8711UXD助力新款Swtich游戏机底座《4K/60HZ投屏方案》

Nintendo Switch&#xff08;OLED版&#xff09;正面搭载了一块分辨率为720P的7.0英寸OLED屏幕&#xff1b;具有白色和电光蓝电光红2种颜色&#xff1b;机身长度102毫米&#xff0c;宽度242毫米&#xff0c;厚度13.9毫米&#xff0c;重量约420克。 [2]Nintendo Switch&#xff…

明天报名!!济宁教师招聘报名照片及常见问题

明天报名!!济宁教师招聘报名照片及常见问题 山东济宁教师招聘1000多人 报名时间: 2024年4月25日9:00-4月28日16:00 缴费时间: 2024年4月25日11:00-4月30日16:00 打印准考证:2024年5月23日9:00-5月26日9:30 初审时间: 2024年4月25日11:00-4月29日16:00 查询时间: 2024年4月…

10、了解JVM判断对象可回收的神秘法则!

10.1、垃圾回收触发时机? 在我们之前的学习中,我们已经了解到,当我们的系统在运行过程中创建对象时,这些对象通常会被优先分配在所谓的“新生代”内存区域,如下图所示。 在新生代中,当对象数量逐渐增多,接近填满整个空间时,会触发垃圾回收机制。这个机制的作用是回收…

人工智能(pytorch)搭建模型28-基于Transformer的端到端目标检测DETR模型的实际应用,DETR的原理与结构

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型28-基于Transformer的端到端目标检测DETR模型的实际应用&#xff0c;DETR的原理与结构。DETR&#xff08;Detected Transformers&#xff09;是一种基于Transformer的端到端目标检测模型&…