最近又碰到了函数传参的问题,每次碰到都会让作者疑惑一段时间,这次干脆总结整理一下,顺便让自己更加透彻的了解传参的问题。咱们开始吧。
c语言中,函数调用时传送给形参表的实参必须与形参有三个一致:类型、个数、顺序,
函数传参有三种方式:值传递,地址传递,引用传递
下面我们首先来介绍值传递:
值传递为最简单的一种,因为它是把实参的值传送给函数局部工作区相应的副本中,函数使用这个副本执行必要的功能。函数修改的是副本的值,实参的值不变。 我们来看代码:
#include<stdio.h> void swap(int m, int n){ int m; m = n; n = temp; } int main(){ int a, b; a = 2; b = 3; swap(a, b); printf("a=%d,b=%d",a,b); } 打印出结果
我们可以看到,a和b在进行交换之前,他们的值分别为2和3,而进行交换操作之后,它们的值仍为2和3,但是在函数swap内部我们可以看到m和n的值确实发生过了交换。
怎么解释这个问题呢,我们可以利用以下代码:
a = 2; m = a; m = m+3; a = ?; m = ?; 这时我们可以看出来,a赋值之后又将a的值赋给了m,这样无论m进行什么操作,都不会影响到a的值。
说到这里我们就可以理解上面的代码了
调用参数的时候,swap(a,b)代表把a和b作为参数传入,在函数swap接收的时候,它们会自动的将a和b的值copy一份给与自己的参数,也就是形参。
换成代码可以如下理解:
swap(a,b); swap(int m = a,int n = b); 所以函数内部进行的所有操作都不会影响到外部。
那么如何才能使得调用函数后实参也发生转换呢?
说到这里,我们就可以引出第二种传参方式:地址传递。
安装配置Docker 下载Docker,点击此处前往Docker下载界面配置Docker镜像加速器。加速器url:https://ozhrm8gx.mirror.aliyuncs.com。加速器配置文件位置:/etc/docker/daemon.json
Docker常用命令 搜索镜像docker search
拉取镜像docker pull
启动容器docker run -itd -p 9980:80 -p 9922:22 -v /usr/local/gitlab-test/etc:/etc/gitlab -v /usr/local/gitlab-test/log:/var/log/gitlab -v /usr/local/gitlab-test/opt:/var/opt/gitlab --privileged=true --name gitlab-test gitlab/gitlab-ce
查看容器进程docker ps
查看镜像列表docker image ls
部署Gitlab 拉取Gitlab镜像。在命令行输入docker pull yrzr/gitlab-ce-arm64v8. 这个是gitlab的arm64镜像在这里插入图片描述配置容器。镜像下载完成后,可在Docker Desktop看到镜像。
点击run,弹出以下界面,配置端口映射和目录挂载后,即可生成一个容器。
启动容器。可在以下界面看到容器,点击播放即可启动该容器进入容器的命令行。 配置Gitlab 以下操作需要在Gitlab容器的命令行进行
配置url。打开gitlab的配置文件vim /etc/gitlab/gitlab.rb 如下,在external_url后面配置gitlab的访问url加载配置。gitlab-ctl reconfigure
本书是世界名著《Software TestAutomation》的姊妹篇,是自动化测试领域的至宝,是每一位测试人员都应该反复研读的一本书!它是Dorothy Gramham、Mark Fewster、LisaCrispin、AlanPage等30多位来自世界各地的自动化测试专家和大师的经验结晶,通过对30多个来自世界各行各业的经典测试案例的分析和研究,讲述了的自动化测试工具、技术、方法,以及自动化测试的实施、引导和管理,不仅包含大量实践,还包含很多失败的教训。让读者不仅能吸取前人留下的宝贵经验和远见卓识,少走弯路,还能更好地领会自动化测试的自然规律——无论是管理人员、测试人员,还是自动化测试人员,本书都非常有价值。
本书一共29个章节,文中只会展示一部分书籍的内容,书籍内容涵盖丰富,需要的朋友点击领取《自动化测试最佳实践》
目录介绍 第 0 章 案例研究反思 / 1
第 1 章 敏捷团队的自动化测试之旅:第一年 / 13
第 2 章 终极数据库自动化 / 25
第 3 章 移动到云端:TiP 的演化——在线的持续回归测试 / 37
第 4 章 Automator 的自动化 / 51
第 5 章 自动化人员自传:从主机到框架的自动化 / 60
第 6 章 项目 1:失败!项目 2:成功! / 76
第 7 章 复杂政府系统的自动化测试 / 93
第 8 章 设备仿真框架 / 103
第 9 章 ESA 项目中基于模型的测试用例生成 / 112
一、概念介绍: 1.数字信号和模拟信号: 在显示生活中,我们经常遇到的如温度、压力、图像等都是模拟量,电子线路中的模拟电压和模拟电流等也都是模拟信号,它是一种连续变化的信号。而数字信号,通常我们无法看到,它通常存储在芯片内,如单片机、计算机、硬盘等。数字信号是离散的信号。
特点幅度变化信号传输方式保密性抗干扰能力处理和存储信号难易模拟信号在时间上和数值上都是连续变化的信号幅度的取值是连续的用模拟量的电压或电流来表示保密性差,通信信号很容易被窃听弱,很容易受到干扰难数字信号在时间上和数值上不连续变化的信号幅度的取值是离散的通过0和1的数字串所构成的数字流数字信号保密性较强,可加密-传输-解密再变换还原成模拟信号抗干扰能力强简单 2.数字信号和模拟信号相互转换的示例 上图是一个打电话的过程,首先,我们通过手机内部的ADC将声音信号(模拟信号)转化为数字信号,然后通过天线将数字信号转换出去,对方手机接收到我们的数字信号之后,通过DAC转换为模拟信号(声音),双方就可以通话了。
下图为AD/DA相互转换的示意:
3. 模拟信号转数字信号: 最低有效位(Least Significant Bit)LSB : 表示ADC芯片最小的一位所代表的模拟量的数值。 步骤:
采样:在时间轴上对信号数字化。按照固定的时间间隔抽取模拟信号的值,采样后就可以使一个时间连续的信号波变为在时间上取值数目有限的离散信号。保持量化编码 奈奎斯特采样定理:采样频率fs.max大于或等于有效信号最高频率fmax的两倍,采样值就可以包含原始信号的所有信号,被采样信号就可以不失真的还原成原始信号。
混叠效应:不同的信号频率在相同的采样频率下,可以得到相同的采样波形。
上图1为原始信号,但是,在混叠效应之下,根据采样点还原原始信号,可以有多种不同频率的信号(如图2)。因此AD转换时需要添加一个抗混叠滤波器,将一些高频谐波干扰杂波过滤掉。只采样需要的波形。
3.1 采样 采样过程示例
3.2 量化 上图为一个三位的ADC,即其所能能表示的分辨率位2的3次方,从000~111。
分辨率:ADC的分辨率指的是对于允许范围内的模拟信号,它能输出离散数字信号值的个数。例如,12位的ADC分辨率即为12位,或者说分辨率为满刻度的1/(2^12)。一个10V满刻度的12位ADC能分辨输入电压变化最小值为: 10V X 1/(2^12) = 2.4mV
转换误差:转换误差通常是以输出误差的最大值形式给出。表示A/D转换器实际输出的数字量和理论上输出的数字量之间的差别。 转换速率:ADC的转换指的是每秒转换的次数。 4 ADC转换方式 4.1 逐次逼近: 4.2 双积分式ADC 二、ESP32 模数转换 ESP32集成了两个12位SAR(逐次逼近寄存器)的ADC,并支持18通道(支持模拟的引脚)的测量。
ADC驱动器API支持ADC1(8个通道,连接到GPIO 32-39)和ADC2(10个通道,连接到GPIO 0、2、4、12-15和25-27)。但是,ADC2的使用对应用程序有一些限制:
Wi-Fi驱动程序使用了ADC2。因此,应用程序只能在未启动Wi-Fi驱动程序时使用ADC2。在一些开发板中ADC2引脚用作捆绑引脚(GPIO 0、2、15),因此不能自由使用。例如GPIO0:用于选择运行方式 ADC配置:
精度设置: typedef enum { ADC_WIDTH_BIT_9 = 0, /*!< ADC capture width is 9Bit*/ ADC_WIDTH_BIT_10 = 1, /*!< ADC capture width is 10Bit*/ ADC_WIDTH_BIT_11 = 2, /*!
SHAP 应用 shap是可视化机器学习模型的一种方法,在使用shap之前,需要训练好特定的模型,然后导入shap库
import shap shap.initjs()#这是为了能够输出shap的图像 接着,将模型输入shap解释器中,创建一个explainer对象,利用它计算每个观察对象的SHAP值,每个特征将对应一个SHAP值。
explainer = shap.Explainer(model) shap_values = explainer(Xtrain) 应用举例 瀑布图 # 为第一个观察对象创建瀑布图! shap.plots.waterfall(shap_values[0]) 在上面这张图中,E[f(x)] = 9.933是基线值,也是平均预测值。最终值是f(x) = 12.668,也是这个对象的预测值。图中每一行代表一个特征,每一行的shap值大小代表了这个特征对于该对象总shap值的贡献。比如,shucked weight就让这个个体的预测值在基线值的基础上增加了1.81。
我们可以为每个对象生成一个瀑布图,每个图都将不一样。
2. 力图
shap.plots.force(shap_values[0]) 力图展示的数据和瀑布图差不多
3. 决策图
瀑布图和力图展示的是个体水平的预测,如果要了解整体模型,我们就要将每个个体的SHAP值进行整合。
# 获取期望值和shap值数组 expected_value = explainer.expected_value shap_array = explainer.shap_values(X) #获取前十个对象的决策图 shap.decision_plot(expected_value, shap_array[0:10],feature_names=list(X.columns)) 在这张决策图中,有十个对象,可以从图像的底端看到,每个对象的起点都是基线值9.933,当我们沿着y轴的特征值向上看的时候,观测对象们的SHAP值开始因为特征贡献的不同而发生变化,并导致最终SHAP的不同。
从这十个对象的决策图,我们可以看到一些规律,比如shucked weight和shell weight对观测对象的SHAP值似乎会产生相反的作用,从而导致图线显示出曲折。
需要注意的是,随着我们输入到决策图中对象的增加,决策图会因为图线太多而开始变得混乱,因此,我们最好限制住输入到决策图中的观测对象数量。
平均SHAP 平均SHAP就是对所有特征对应对象的SHAP值取绝对值后相加,下面以柱状图的形式展现。
shap.plots.bar(shap_values) 由图可见,在平均意义上,shell weight是对模型的预测具有最大影响力的一个特征。从这个意义上来讲,平均SHAP图可以视为特征重要性图。需要注意的是,上面这张图不能够告诉我们具体地,特征与模型是如何交互的。
蜂窝图
蜂窝图也是将所有的SHAP值进行综合展示的方法。在这种图中,左侧的标签是特征,并且它们也是按照重要性排序的,这一点上和平均SHAP图一样。不同的是,蜂窝图中的每一点都代表一个真实的样本。对于每一组(每一行)来说,数据点的颜色是由特征的值决定的。特征的值越大,点的颜色越红。相同SHAP值的点越多,那么“蜂窝”的截面积就越大,看起来就会越粗。 # Beeswarm plot shap.plots.beeswarm(shap_values) 蜂窝图解决了前面几张图的一些弊端。它允许我们在看到特征重要性的同时,对于特征如何影响整体预测值有一个直观的判断。比如,在蜂窝图中,shell weight与预测值呈正相关关系,shucked weight则呈现负相关。
如果我们想要进一步探究一下这两个特征与模型的关系,可以绘制散点图。
fig, ax = plt.subplots(nrows=1, ncols=2,figsize=(16,8)) #SHAP scatter plots shap.
网上很多类似的文章,但可能没有一些实际压测的说明,这里做简单说明
配置 #统一在http域中进行配置 #限制请求 limit_req_zone $uri zone=api_read:20m rate=50r/s; #按ip配置一个连接 zone limit_conn_zone $binary_remote_addr zone=perip_conn:10m; #按server配置一个连接 zone limit_conn_zone $server_name zone=perserver_conn:100m; ===== server ===== location / { if (!-e $request_filename){ rewrite ^/(.*) /index.php last; } #请求限流排队通过 burst默认是0 limit_req zone=api_read burst=100; #连接数限制,每个IP并发请求为50 limit_conn perip_conn 50; #服务所限制的连接数(即限制了该server并发连接数量) limit_conn perserver_conn 200; #连接限速 #limit_rate 100k; } 压测效果 1. 未限制 1000 个请求并发100 个客户端 1000 个请求并发1000 个客户端 并发100、1000,每秒能处理的请求数相近,因为这次目的不是压测nginx 性能,所以没必要继续往下压,这里压测主要是跟后面限流后的数据做对比。
2. 配置限流 rate=50r/s # 每秒新增50个令牌 burst=100 # 令牌桶一共有100个令牌 perip_conn 50 # 每个IP最多并发50个连接 perserver_conn 200 # 限制该server并发连接数 1000 个请求并发100 个客户端 虽然请求没有失败,但是明显地RPS 下降很明显,请求等待耗时也比不限流要多。
wx.redirectTo:关闭当前页,跳转到指定页; wx.navigateTo:保留当前页,跳转到指定页; wx.reLaunch:关闭所有页面,打开到应用内的某个页面。
wx.switchTap:只能用于跳转到tabbar页面,并关闭其他非tabbar页面。
wx.navigateBack:关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages()获取当前的页面栈,决定需要返回几层。
1.读取数据
> data <- read.csv("主成分分析数据.csv")
> View(data)
> View(data)
> head(data)
2.数据标准化
> data1 <- data[,2:10]
> head(data1)
> data2<- prcomp(data1,scale =TRUE )
> View(data2)
> data2
3.均值
> data2$center
4.标准差
> data2$scale
5.矩阵(主成分上的值)
> data2$x
6.每个主成分的权重
> data2$rotation
7.data2列表图(变量的权重)
> biplot(data2,scale = 0)
8.数据解释
> summary(data2)
使用不同剂量的药物对小鼠进行处理怀孕时间对产下小鼠体重的影响协方差分析 1.载入multcomp包
> library(multcomp)
载入需要的程辑包:mvtnorm
载入需要的程辑包:survival
载入需要的程辑包:TH.data
载入需要的程辑包:MASS
载入程辑包:‘TH.data’
The following object is masked from ‘package:MASS’:
geyser
2.使用multcomp包中的litter数据集
> litter
3.统计分组情况
> table(litter$dose)
4.分组统计平均数
> attach(litter)
> aggregate(weight, by=list(dose), FUN=mean)
结果分析:不同处理组体重不同
5.定义fit变量进行方差分析
> fit <- aov(weight ~gesttime+dose,data=litter) #gesttime为协变量
> summary(fit)
Df Sum Sq Mean Sq F value Pr(>F) gesttime 1 134.3 134.30 8.049 0.00597 **
dose 3 137.1 45.71 2.739 0.04988 * Residuals 69 1151.3 16.69 ---
Signif. codes: 0 ‘***’ 0.
对于画流程图的工具,之前大家用得比较多的可能是:visio和process on了。
visio是微软的一款画图软件,需要在电脑上安装,正版软件是付费的。
process on是一款免费在线画图工具,功能非常强大。但有个缺点是只能免费保存最近9张图,想保留更多的图,需要付费升级。
draw.io,它已经成为了我画图首选工具。https://www.diagrams.net/
基本能覆盖你的所需,done!
性能问题呈现给用户的感受往往是简单而直接的:加载资源缓慢、运行过程卡顿或响应交互迟缓等,当把这些问题呈现到前端工程师面前时,却是另一种系统级别复杂的图景。
从域名解析、TCP 建立连接到 HTTP 的请求与响应,以及从资源请求、文件解析到关键渲染路径等,每一个环节都有可能因为设计不当、考虑不周、运行出错而产生性能不佳的体验。作为前端工程师,为了能在遇到性能问题时快速而准确地定位问题所在,并设计可行的优化方案,熟悉前端页面的生命周期是一堂必修课。
从浏览器地址栏输入URL后,到页面渲染出来,整个过程都发生了什么? (1)浏览器接收到 URL,到网络请求线程的开启。
(2)一个完整的 HTTP 请求并的发出。
(3)服务器接收到请求并转到具体的处理后台。
(4)前后台之间的 HTTP 交互和涉及的缓存机制。
(5)浏览器接收到数据包后的关键渲染路径。
(6)JS 引擎的解析过程。
网络请求线程开启 浏览器接收到我们输入的 URL 到开启网络请求线程,这个阶段是在浏览器内部完成的,需要先来了解这里面涉及的一些概念。
首先是对 URL 的解析,它的各部分的含义如下图所示。
URL结构:Protocol://Host:Port/Path?Query#Fragment,例如 http://example.com/users/1?foo=bar#
解析 URL 后,如果是 HTTP 协议,则浏览器会新建一个网络请求线程去下载所需的资源,要明白这个过程需要先了解进程和线程之间的区别,以及目前主流的多进程浏览器结构。
进程与线程 简单来说,进程就是一个程序运行的实例,操作系统会为进程创建独立的内存,用来存放运行所需的代码和数据;而线程是进程的组成部分,每个进程至少有一个主线程及可能的若干子线程,这些线程由所属的进程进行启动和管理。由于多个线程可以共享操作系统为其所属的同一个进程所分配的资源,所以多线程的并行处理能有效提高程序的运行效率。
进程和线程之间的区别:
(1)只要某个线程执行出错,将会导致整个进程崩溃。
(2)进程与进程之间相互隔离。这保证了当一个进程挂起或崩溃的情况发生时,并不会影响其他进程的正常运行,虽然每个进程只能访问系统分配给自己的资源,但可以通过 IPC 机制进行进程间通信。
(3)进程所占用的资源会在其关闭后由操作系统回收。即使进程中存在某个线程产生的内存泄漏,当进程退出时,相关的内存资源也会被回收。
(4)线程之间可以共享所属进程的数据。
单进程浏览器 在熟悉了进程和线程之间的区别后,我们在此基础上通过了解浏览器架构模型的演变,来看看网络请求线程的开启处在怎样的位置。
说到底浏览器也只是一个运行在操作系统上的程序,那么它的运行单位就是进程,而早在 2008 年谷歌发布 Chrome 多进程浏览器之前,市面上几乎所有浏览器都是单进程的,它们将所有功能模块都运行在同一个进程中,其架构示意图如下图所示。
单进程浏览器在以下方面有着较为明显的隐患。
●流畅性:首先是页面内存泄漏,浏览器内核通常非常复杂,单进程浏览器打开再关闭一个页面的操作,通常会有一些内存不能完全回收,这样随着使用时间延长,占用的内存会越来越多,从而引起浏览器运行变慢;其次由于很多模块运行在同一个线程中,如JS引擎、页面渲染及插件等,那么执行某个循环任务的模块就会阻塞其他模块的任务执行,这样难免会有卡顿的现象发生。
●安全性:由于插件的存在,不免其中有些恶意脚本会利用浏览器漏洞来获取系统权限,进行引发安全问题的行为。
●稳定性:由于所有模块都运行在同一个进程中,对于稍复杂的JS代码,如果页面渲染引擎崩溃,就会导致整个浏览器崩溃。同样,各种不稳定的第三方插件,也是导致浏览器崩溃的隐患。
多进程浏览器 出于对单进程浏览器存在问题的优化,Chrome 推出了多进程浏览器架构,浏览器把原先单进程内功能相对独立的模块抽离为单个进程处理对应的任务,主要分为以下几种进程。
(1)浏览器主进程:一个浏览器只有一个主进程,负责如菜单栏、标题栏等界面显示,文件访问,前进后退,以及子进程管理等。
(2)GPU 进程:GPU(图形处理单元)最初是为了实现 3D 的 CSS 效果而引入的,后来随着网页及浏览器在界面中的使用需求越来越普遍,Chrome 便在架构中加入了 GPU 进程。
(3)插件进程:主进程会为每个加入浏览器的插件开辟独立的子进程,由于进程间所分配的运行资源相对独立,所以即便某个插件进程意外崩溃,也不至于对浏览器和页面造成影响。另外,出于对安全因素的考虑,这里采用了沙箱模式(即上图中虚线所标出的进程),在沙箱中运行的程序受到一些限制:不能读取敏感位置的数据,也不能在硬盘上写入数据。这样即使插件运行了恶意脚本,也无法获取系统权限。
(4)网络进程:负责页面的网络资源加载,之前属于浏览器主进程中的一个模块,最近才独立出来。
(5)渲染进程:也称为浏览器内核,其默认会为每个标签窗口页开辟一个独立的渲染进程,负责将 HTML、CSS 和 JavaScript 等资源转为可交互的页面,其中包含多个子线程,即 JS 引擎线程、GUI 渲染线程、事件触发线程、定时触发器线程、异步 HTTP 请求线程等。当打开一个标签页输入 URL 后,所发起的网络请求就是从这个进程开始的。另外,出于对安全性的考虑,渲染进程也被放入沙箱中。
【操作系统/计组】页面大小 与 页表项 结论1(一级页表)结论2(二级、多级页表) 例题 首先,不论一级页表还是多级页表: 页面大小 = 2^(页内地址位数) 页号有多少,页就有多少个 用于存放页的地址空间就可以写为 页号页内地址(或叫位移量) 接下来,分别来看一级页表和多级页表
1. 一级页表
页表是系统为每个进程建立的页面映射表
进程中的每一页在页表中都有一项,这个项就是页表项,大小可以理解为页表中一行的大小
每个页表项的结构为:
页号物理块号 此时,存储页号和物理块号的空间大小是页表项的大小
整个页表的内容就是:
页号物理块号页号物理块号页号物理块号页号物理块号………… 易知,页表大小 = 页表项的大小 x 页表项的个数
结论1(一级页表) 在一级页表,可以认为页表项大小与页面大小没有关系
2.二级页表
当页面数量很多时,需要标很多页号,导致页号占的位数很多。
如果将所有的页号及对应的物理块号写入一个页表,页表会非常长,页表需要占据太多连续地址空间,而内存空间不够,因此需要将一页拆分
将原先页号的部分
页号 按照原始页面大小,拆分为页号 和 “页面”。注意,还是行使页表的功能,实现页号与物理块号的映射,只是改变了结构
页号页面 现在拥有存放页的地址空间就变成了
页号页面1页面2 将现在的页号命名为【外层页号】
将从页号拆分出来的页面1命名为【外层页】
原始的页面视为【内层页】
外层页号外层页内层页 接下来看位数
内层页还是原始页面的大小
上文提到,外层页面大小 = 内层页面大小
但外层页是要履行页表功能,存放内层页的索引,页表项还是“一行”的大小
因此外层页的位数 = log2(内层页面大小 / 页表项的大小),即外层页还是表示存放页表项的个数
这就是页面大小与页表项大小的关系
在课本中,二级页表结构为
外层页号外层页内地址页内地址 结论2(二级、多级页表) 1)外层页号对外层页的计数,存放外层页号,位数 = log2(外层页个数)
2) 外层页存放的页表项是内层页的索引,外层页大小 = 内层页大小,外层页内地址位数 = log2(页面大小 / 页表项的大小)
There are no licenses to review. Either the software does not specify a license, or the license has already been reviewed and approved.
eclipse安装插件遇到,
其实是之前安装的时候已经点了接受, 可能在安装过程中, 看eclipse ide右下角的状态是不是在安装中, 遇到上述提示的时候点finish就会安装, 看右下角的进度提示。
目录
1.知识背景
2.Hexview介绍
3.Hexview命令介绍
3.1对齐
3.2合并两个BIN文件
3.3计算特定区域CRC32并写入到指定地址
1.知识背景 汽车行业ECU基本都具有FBL(flash boot loader)功能。FBL可以基于UDS协议实现刷写功能。很多OEM厂商都有OTA的需求,但是对于整车内部ECU来说,仍然是基于UDS协议实现刷写。但是往往会要求刷写文件具有特定格式。如吉利/沃尔沃采用VBF(volov binary format)格式,吉利提供了VBF生成工具。长城要求具有特殊hearer area等特殊格式的BIN文件。
这时候往往需要对IDE编译产生的S19、HEX、BIN等格式进行一些格式操作,已满足主机厂刷写文件要求。或方便集成项目APP。
本文介绍Vector Hex view命令行模式对BIN文件进行一些常规操作,如对齐、填充、CRC计算。
2.Hexview介绍 HexView可以打开各种二进制格式、如最常见SREC、HEX、BIN。打开界面如下。可以在界面中完成各种操作,如对其、填充、删除等。但是项目开发往往需要使用方便快捷方式快速转换至项目需要的格式,所有可以使用BAT批处理加hexview命令行方式进行脚本开发,一键生成项目需要个刷写文件格式。
3.Hexview命令介绍 例如有一个app.bin文件,基于这个文件需要做以下处理。hexview.exe替换为自己真实路径。
3.1对齐 为了方便Flash刷写,经常会对刷写内容进行对齐,如16字节对齐。可以在bat文件写入如下命令:
..\core\HexView\hexview.exe /s app.bin /AD:0xFF /AL:0x10 /xb -o app.bin
3.2合并两个BIN文件 一般项目都会有FBL和APP两个BIN文件,如把APP往后偏移0x1000然和合并FBL,可以使用下面命令合并两个文件。不同项目根据芯片Flash和APP划分,调整偏移参数。
..\core\HexView\hexview.exe /s /mt:ota.bin+app.bin;0x1000 /xb -o out.bin
如果需要截取APP一段地址参与Merge,可以使用下面的命令:
..\core\HexView\hexview.exe /s /mt:ota.bin+app.bin;0x1000:0x0-0x100 /xb -o out.bin
3.3计算特定区域CRC32并写入到指定地址 HexView Calculation of the CRC-32 according to IEEE, using the polynomial: 0x04C11DB7. The start value is 0xFFFFFFFF. The result is inverted.
带I2C的LCD1602液晶显示51单片机程序
实现功能:液晶屏上显示日期及动态时间,由中断函数来实现时间的动态变换
#include <reg52.h>//头文件 #include <intrins.h> #define uint unsigned int #define uchar unsigned char #define C51_SCL P3^0 //SCL引脚 #define C51_SDA P3^1 //SDA引脚 #define ADDR 0X4E //设备地址 #define String_len1 16 //液晶显示第一行字符串长度 #define String_len2 16 //液晶显示第二行字符串长度 uchar miao,shi,fen; //显示时分秒 uchar count; /*******************************************/ sbit SCL = C51_SCL; //I2C串口 sbit SDA = C51_SDA; //I2C串口 uchar code table[]="21-7-30 "; //LCD初始化显示内容 uchar code table1[]="23:59:45 "; /*********************延时函数*************************************/ static void delay_us() { ;; //用两个空语句实现短时间延时,当晶振为11.0592MHz时,约4~5微秒 } void delay(uchar n) { int i,j; for(i=0;i<n;i++) for(j=0;j<120;j++); } /**********************************************/ /************IIC协议的起始信号*****************/ void IIC_Start() { SDA=1; SCL=1; delay_us(); SDA=0; delay_us(); } /** IIC协议的应答信号 * @param None * @retval None */ void IIC_Ack() { uchar i; SCL=1; delay_us(); while((SDA==1)&&(i<250))i++; SCL=0; delay_us(); } /** * @brief 写入一个字节到I2C总线 * @param date:将要被写入的数据 * @retval None */ void IIC_Write_Byte(uchar date) { uchar i,temp; temp=date; for(i=0;i<8;i++) { temp=temp<<1; SCL=0; delay_us(); SDA=CY; delay_us(); SCL=1; delay_us(); } SCL=0; delay_us(); SDA=1; delay_us(); } /** * @brief 通过IIC协议写一个命令到1602液晶上 * @param comm:将要被写入的命令 * @retval None */ void IIC_Write_Comm_LCD(uchar comm) { uchar data_h = comm & 0xf0; uchar data_l = (comm & 0x0f) << 4; IIC_Write_Byte(0x00+data_h); IIC_Ack(); IIC_Write_Byte(0x04+data_h); IIC_Ack(); IIC_Write_Byte(0x00+data_h); IIC_Ack(); delay(5); IIC_Write_Byte(0x00+data_l); IIC_Ack(); IIC_Write_Byte(0x04+data_l); IIC_Ack(); IIC_Write_Byte(0x00+data_l); IIC_Ack(); delay(5); } /** * @brief 通过IIC写一个数据到1602液晶上 * @param date:将要被写入的数据 * @retval None */ void IIC_Write_Date_LCD(uchar date) { uchar data_h = date & 0xf0; uchar data_l = (date & 0x0f) << 4; IIC_Write_Byte(0x01+data_h); IIC_Ack(); IIC_Write_Byte(0x05+data_h); IIC_Ack(); IIC_Write_Byte(0x01+data_h); IIC_Ack(); delay(5); IIC_Write_Byte(0x01+data_l); IIC_Ack(); IIC_Write_Byte(0x05+data_l); IIC_Ack(); IIC_Write_Byte(0x01+data_l); IIC_Ack(); delay(5); } /********************************************************/ /** * @brief 初始化1602液晶,由于只有一个设备,所以一直占用总线 * @param None * @retval None */ /***********************1602程序初始化函数*************************************/ void LCD1602_Init() { uchar i = 0; IIC_Start(); IIC_Write_Byte(ADDR); IIC_Ack(); IIC_Write_Comm_LCD(0x02); //设置四线发送数据 IIC_Write_Comm_LCD(0x28); //设置显示模式 IIC_Write_Comm_LCD(0x08); //显示关闭 IIC_Write_Comm_LCD(0x0c); //设置光标开关 IIC_Write_Comm_LCD(0x06); //设置光标移动 IIC_Write_Comm_LCD(0x01); //清屏 for(i=0;i<String_len1;i++) //显示预先设定值 { IIC_Write_Date_LCD(table[i]); } IIC_Write_Comm_LCD(0xc0); for(i=0;i<String_len2;i++) { IIC_Write_Date_LCD(table1[i]); } TMOD=0x01; //定时器 TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; ET0=1; TR0=1; } /**********************显示时分**********************************/ void write_sfm(uchar add,uchar date) { uchar shi,ge; shi=date/10; ge=date%10; IIC_Write_Comm_LCD(0x80+0x40+add); IIC_Write_Date_LCD(0x30+shi); IIC_Write_Date_LCD(0x30+ge); } /*******************主函数***********************************/ void main() { LCD1602_Init();//LCD1602初始化程序 while(1); } /**************************中断程序**************************/ void timer0() interrupt 1//实现时分秒的动态变换 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; count++; if(count==18) { count=0; miao++; if(miao==60) { miao=0; fen++; if(fen==60) { fen=0; shi++; if(shi==24) { shi=0; } write_sfm(0,shi);//待修改液晶屏的地址及数据 } write_sfm(3,fen); } write_sfm(6,miao); } }
1、新建一个请求
2、输入url
3、选择请求方法为get
4、填写请求头参数
5、填写请求参数,get请求参数填写在Params中
6、发送请求
7、查看响应结果
demo地址:
https://github.com/939624959/Components-time-picker-.git
目录
效果图: 组件 time-picker
time-picker.json
time-picker.js
time-picker.wxml
time-picker.wxss
页面
page.json
page.js page.wxml
page.wxss
效果图: 组件 time-picker time-picker.json { "component": true, "usingComponents": { "van-popup": "@vant/weapp/popup/index", "van-icon": "@vant/weapp/icon/index", "van-tree-select": "@vant/weapp/tree-select/index", "van-button": "@vant/weapp/button/index" } } time-picker.js const getDateStr = AddDayCount => { var dd = new Date(); dd.setDate(dd.getDate()+AddDayCount);//获取AddDayCount天后的日期 var y = dd.getFullYear(); var m = dd.getMonth()+1;//获取当前月份的日期 var d = dd.getDate(); return y+"-"+m+"-"+d; } Component({ properties: { title: String, // 标题 isShow: Boolean, // 控制显示隐藏 dayNum: Number, // 左侧导航栏数量 timeArr: Array // 右侧时间段数组 }, data: { mainActiveIndex: 0, activeId: null, itemTree:[] }, lifetimes: { attached: function() { // 在组件实例进入页面节点树时执行 this.
环境 window10简体中文,所以系统环境是:GB2312,代码页936。(因为我没有去改过系统的默认编码)
vs工程中没有设置 源字符串集,即没有设置/source-charset,当然也没有设置 /utf-8。(另外,vs工程中,也可以单独对某个文件进行 字符集 设置的。也没有进行这样的设置)
工程中大部分的文件都是按照GB2312编码来存储的。
问题产生 此时工程中以前存在一个utf-8编码来存储的文件。但文件添加了一行(并且,这个中文字符的硬编码是必须的。也就是说,以前这个文件没出问题是因为里面只有英文字符)
std::string sss = "廿"; 然后编译报错:常量中有换行符。
问题解决 解决方法当然就是把这个特殊文件转成GB2312编码的存储。因为工程没有设置字符集,MSVC在默认情况下是用系统编码,在简体中文的Win10系统中,默认编码就是GB2312。所以MSVC使用的源字符集就是GB2312。
简单的说,就是让文件的存储编码方式,和编译器使用的源字符集,是一样的。
修改文件编码遇到的问题 首先我通过高级保存选项来保存,但还是会出错。(实际上没有转换成功)
通过notepad的使用GB2312编码,然后保存。但还是会出错。(实际上没有转换成功)
最后通过notepad的转为ANSI编码,然后保存,转换成功。(因为系统此时的默认编码就是GB2312,所以转为ANSI编码也就是转为GB2312编码)
后记 不知道为什么只有“转为ANSI编码”才可以转换成功,有好心人知道的可以说一下。
总之,通过在字符串后加个空格可以让std::string sss = "廿 ";编译通过,但这不是正确的做法。但我通过这个做法,打印出了std::string sss = "廿 ";这个字符串的各个字节,然后就基本看出了问题所在,是因为——文件是utf-8存储,但使用的源字符集为GB2312。
文章目录 Flink Checkpoint超时问题 问题现象问题分析 问题1:TaskManager进程挂掉问题2:任务长时间处于CANCELING问题3:Checkpoint超时问题4:数据无法正常同步 解决思路总结参考文档 问题现象 业务部门最近使用Flink来做数据实时同步,通过同步工具把CDC消息接入Kafka,其中上百张表同步到单个topic里,然后通过Flink来消费Kafka,做数据解析、数据分发、然后发送到目标数据库(mysql/oracle),整个链路相对比较简单,之前通过Jstorm来实现,最近才迁移到Flink,通过Flink DataStream API来实现。代码里仅用到Kafka Source、Map、Process几个简单的算子,发送目标库的逻辑在Process的逻辑里实现,因此process的逻辑里涉及数据库连接的创建与清理、通过队列来缓存数据,创建额外线程来启动发送和消费队列的逻辑,先不说整个逻辑是否合理,本文主要基于此案例来阐述遇到的问题和排查思路以及解决方法。
业务部门使用的Flink版本为1.11.2,部署模式采用Standalone。出问题的是单机环境,即有一个JobManager进程和一个TaskManager进程。
问题现象是通过web页面观察发现启动任务后很短时间任务就发生重启,同时还会出现重启去Cancel任务的时候无法Cancel,一直处于CANCELING状态(正常会很快变成CANCELED)。并且过一段时候后TaskManager进程挂掉,导致任务一直处于无法申请Slot的状态,最终导致数据无法正常同步。因此,问题主要有以下几个现象:
Checkpoint超时子任务长时间处于CANCELING,任务长时间处于RESTARTING状态一段时间后TaskManager进程挂掉数据无法正常同步 问题分析 问题1:TaskManager进程挂掉 看到问题的第一反应是首先看TaskManager进程为什么会挂掉,这个问题比较严重,因为涉及到集群层面而不单单是任务了。查看Taskmanager日志,发现有以下片段:
// 日志1 ERROR org.apache.flink.runtime.taskexecutor.TaskExecutor [] - Task did not exit gracefully within 180 + seconds. org.apache.flink.util.FlinkRuntimeException: Task did not exit gracefully within 180 + seconds. at org.apache.flink.runtime.taskmanager.Task$TaskCancelerWatchDog.run(Task.java:1572) [flink-dist_2.11-1.11.2.jar:1.11.2] at java.lang.Thread.run(Thread.java:748) [?:1.8.0_181] 2021-03-05 04:09:30,816 ERROR org.apache.flink.runtime.taskexecutor.TaskManagerRunner [] - Fatal error occurred while executing the TaskManager. Shutting it down... org.apache.flink.util.FlinkRuntimeException: Task did not exit gracefully within 180 + seconds.
正常在学习一个新框架之前, 肯定要先调研下这个框架究竟能做些什么事吧?
但对于 streamlit 来说,请你相信我,这是一个你可以无脑去学习的框架,我之所以这么说,是因为我相信终有一天,你一定能用得上它。
如果你真的需要一些理由的话,那我随便给你举几个例子:
做数据分析的同学,想要把数据分析的成果做成应用展示给其他人,怎么办?想做一些用户数据的收集,但某些公有平台又却仅有收集,没有对应的开发能力提供数据的处理与反馈,怎么办? 难道真的要为了这种简单的需求,去折腾 html + css + js + flask (or django) 吗?
这是大多数非专业开发者的痛点,也是 streamlit 这个框架流行开来的主要原因。
Streamlit 是一个用于机器学习、数据可视化的 Python 框架,它能几行代码就构建出一个精美的在线 app 应用。
它能做什么,取决于你想干什么?
streamlit 的功能强大,要学习的函数虽然多,但非常容易上手,学习成本却远比 前端+Flask 来得低得低。接下来,我会一一介绍。
1. 如何安装? 和安装其他包一样,安装 streamlit 非常简单,一条命令即可
➜ pip install streamlit 考虑到 streamlit 会附带安装比较多的工具依赖包,为了不污染当前的主要环境,我使用 venv 新建一个虚拟环境。
➜ python3 -m venv . 然后使用如下命令进入该虚拟环境
➜ source ./venv/bin/activate 接下来,再安装 streamlit ,命令在上边。
安装的包比较多(数了下竟然接近 92 个?),过程也会很久,需要点耐心
➜ pip list | wc -l 92 在安装过程中,可能会遇到一些问题,但也不一定,这取决于你的机器,如遇到问题请自行借助搜索引擎解决。
参考马士兵老师Java集合全套视频
Java集合 【1】数组,集合都是对多个数据进行存储操作的,简称为容器(PS:这里的存储指的是内存层面的存储,而不是持久化存储。)
【2】数组:特点:
数组一旦制定了长度,那么长度就确定了,不可以更改。数组一旦声明了类型以后,数组中只能存放这个类型的数据。数组中只能存放同一种类型的数据。 【3】数组:缺点:
数组长度不可以更改。删除、增加元素效率低。数组中实际元素的数量是没有办法获取的,没有提供对应的方法或者属性来获取的。数组存储:有序,可重复,对于无序的,不可重复的需求数组不可以满足。 【4】正是由于上面的缺点,引入了一个新的存储数据的结构->集合
【5】集合一章会学习很多集合,为什么要学习这么多集合呢?
因为不同集合底层数据结构不一样。
【简要的集合结构图】
1.Collection接口 import java.util.*; public class javaCollection { public static void main(String[] args) { /* Collection的常用方法: 增加:add(E e) addAll(Collection<? extends E> c) 删除:clear() remove(Object o) 修改: 查看:iterator() size() 判断:contains(Object o) containsAll(Collection<?> c) equals(Object o) */ //创建对象:接口不能创建对线,利用实现类创建对象 Collection col = new ArrayList(); //调用方法: //集合有一个特点:只能存放引用数据类型的数据,不能是基本数据类型 col.add(18);//基本数据类型自动装箱 col.add(12); col.add(11); col.add(17); col.add("abc"); System.out.println(col); List list = Arrays.asList(new Integer[]{1,2,3,4,5}); col.addAll(list);//将另一个集合添加到col中 System.out.println(col); //集合中数据是否被删除 System.out.println("集合是否包含元素5:"+col.contains(5)); boolean isRemove = col.
人工智能系统的目的 提供更加高效的编程语言、框架和工具。
更具表达能力和简洁的神经网络计算原语和编程语言更直观的编辑、调试和实验工具整个深度学习生命周期中的系统问题:模型压缩、推理、安全、隐私保护等提供全面的学习系统:强化学习、自动机器学习等 提供更强大和可扩展的计算能力。
自动编译优化算法自动推导计算图根据不同体系结构自动并行化自动分布式化,并扩展到多个计算节点持续优化模型效果
探索并解决新挑战下的系统设计、实现和演化的问题。 人工智能系统基本组成部分 体验:拥有端到端的人工智能用户体验-模型、算法、Pipeline、实验、工具、生命周期管理框架:包括编程接口、计算图、自动梯度计算、IR(中间表示)、编译器基础设施运行时:包括深度学习运行时-优化器、规划器、执行器架构(单节点和云):包含硬件API(GPU、CPU、FPGA、ASIC)、资源管理/调度器、可扩展的网络堆栈(RDMA、IB、NVLink) 人工智能系统生态 首先是核心系统软硬件,其主要包含:
深度学习任务运行和优化环境通用资源管理和调度系统新型硬件及相关高性能网络和计算栈 在此之上,我们有深度学习算法和框架,其主要包含:
广泛用途的高效新型通用AI算法多种深度学习框架的支持与进化深度神经网络编译架构及优化 最后是,更广泛的Al系统生态,主要有:
机器学习新模式(RL)自动机器学习(AutoML)安全与隐私模型推导、压缩与优化 因此,如果我们要系统的解决问题意味着需要全面深入的了解整个问题空间,因为任何维度的短板都可能会影响整个系统。
一个典型的AI系统平台样例 以OpenPAI为例,下面展示了一个典型的人工智能平台所包含的基本功能,具体如下:
提供接口和工具供用户提交训练任务提供文件管理系统管理训练所需的大数据提供运行环境供深度学习框架访问 计算资源:GPU/FPGA/ASIC/TPU/CPU 网络资源:IB/RDMA 存储资源:HDFS/NFS/Ceph高效的调度算法 分配异构计算资源 错误恢复和容错管理日志、 性能监控系统用户、 安全管理 你好,我叫果冻,创作不易,如果觉得我的博文能够帮助到你,期待你的点赞,祝好~~~
1. 连接数据库
通过依赖注入配置应用程序,通过startup类的ConfigureService方法中的AddDbContext将EFCore添加到依赖注入容器
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext(
options => options.UseMySql(Configuration["DbConfig:Mysql:ConnectionString"]);
}
将名为 OpenDbContext的 DbContext 子类注册到依赖注入容器的Scope生命周期。上下文配置为使用MySQL数据库提供程序,并从配置中读取数据库连接字符串。
OpenDbContext类必须公开具有 DbContextOptions参数的公共构造函数。这是将 AddDbContext 的上下文配置传递到 DbContext 的方式。
public class OpenDbContext : DbContext
{
public OpenDbContext(DbContextOptions options) : base(options)
{
}
public DbSet Users { get; set; }
public DbSet Scores { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//另一种配置连接数据库的方式
//optionsBuilder.UseMySql("连接数据库", ServerVersion.AutoDetect("连接数据库字符串"));
//显示敏感数据日志
optionsBuilder.EnableSensitiveDataLogging(true);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
关于ORB、FREAK、BRISK、AKAZE的具体原理参考原文,最近没时间翻译,后续会花时间把原文翻译放在博客里面。这些特征点提取方法都可以在opencv里面调用函数实现。
特征点提取与匹配原文论文下载_nudt一枚研究生-CSDN博客
feature_extraction.cpp #include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/highgui/highgui.hpp> #include <chrono> #include <opencv2/imgproc.hpp> #include <opencv2/flann.hpp> #include <opencv2/xfeatures2d.hpp> using namespace std; using namespace cv; using namespace cv::xfeatures2d; // FAST特征提取 void FAST_feature(const Mat& img, std::vector<KeyPoint>& keypoints); //orb特征提取与匹配 void ORB_feature_matches ( const Mat& img_1, const Mat& img_2, std::vector<KeyPoint>& keypoints_1, std::vector<KeyPoint>& keypoints_2, std::vector< DMatch >& matches ); //BRISK 特征提取与匹配 void BRISK_feature_matches ( const Mat& img_1, const Mat& img_2, std::vector<KeyPoint>& keypoints_1, std::vector<KeyPoint>& keypoints_2, std::vector< DMatch >& matches ); //AKAZE 特征提取与匹配 void AKAZE_feature_matches ( const Mat& img_1, const Mat& img_2, std::vector<KeyPoint>& keypoints_1, std::vector<KeyPoint>& keypoints_2, std::vector< DMatch >& matches ); //FREAK 特征提取与匹配 void FREAK_feature_matches ( const Mat& img_1, const Mat& img_2, std::vector<KeyPoint>& keypoints_1, std::vector<KeyPoint>& keypoints_2, std::vector< DMatch >& matches ); int main ( int argc, char** argv ) { if ( argc !
在使用element-ui的Dialog时,默认会有遮罩层,想要去除遮罩层,官方文档给出了 modal 属性;
modal 是一个布尔值,表示是否需要遮罩层;
然而,将 modal 赋值为 false 时并不生效;
这里官方文档也给出了提示:
当 modal 的值为 false 时,请一定要确保 append-to-body 属性为 true,由于 Dialog 使用 position: relative 定位,当外层的遮罩层被移除时,Dialog 则会根据当前 DOM 上的祖先节点来定位,因此可能造成定位问题。
由于定位问题,我们还需将 append-to-body属性置为 true;
本以为事情就这样结束了,然而,事情并不那么简单,此时遮罩层依然存在;
重点来了,我们还需要使用 v-bind 指令来绑定 modal 属性,才能将遮罩层去除;
<el-dialog :modal="false" append-to-body="true"></el-dialog> 至此,我们就成功的去除了 Dialog 对话框的遮罩层;
<el-button type="text" @click="centerDialogVisible = true" >点击打开 Dialog</el-button > <el-dialog title="dialog去掉遮罩层" v-model="centerDialogVisible" width="30%" center :modal="false" append-to-body="true"> <span>注意:modal需要用v-bind指令绑定</span> <template #footer> <span class="dialog-footer"> <el-button type="primary" @click="centerDialogVisible = false" >确定</el-button> </span> </template> </el-dialog> 如果小伙伴们有更好的解决方法,欢迎在评论区留言~~~
1.打开SQL Server Management Studio。点到“作业”
2.按“F7”,可以看到“对象资源管理器详细信息”
3.Ctrl+A全选。创建脚本,把脚本在新数据库执行即可。
目录
微服务简介
背景分析
什么是微服务
SpringCloud Alibaba微服务解决方案
概述
核心组件分析
解决方案架构设计
构建SpringCloud 聚合项目并进行环境初始化
工程结构
创建空项目
项目初始化配置
创建聚合父工程
创建服务提供方模块
创建服务消费方模块
创建API网关服务模块
总结(Summary)
核心知识点
常见问题分析 常见Bug分析 微服务简介 背景分析 讲微服务之前,我们先分析以下单体应用。所谓单体应用一般是基于idea/eclipse,maven等建一个工程,然后基于SpringBoot,spring,mybatis框架进行整合,接下来再写一堆dao、mapper、service、controller,再加上一些的配置文件,有可能还会引入redis、elasticsearch、mq等其它项目的依赖,开发好之后再将项目打包成一个jar包/war包。然后再将包扔到类似tomcat这样的web服务中,最后部署到公司提供给你的linux服务器上。 接下来,你针对服务提供的访问端口(例如8080端口)发起http请求,请求会由tomcat直接转交给你的spring web组件,进行一层一层的代码调用。对于这样的设计一般适合企业的内部应用,访问量不大,体积比较小,5人以内的团队即可开发和维护。但对于一些大型互联网项目,假如需要10人以上的开发和维护团队,仅频繁的创建代码分支,编写业务功能,然后合并分支,就会出现很多代码冲突。每次解决这些大量的代码冲突,可能就会耗费好几天的时间。基于这样的背景微服务诞生了.
在微服务架构设计中,建议超出需要10人开发和维护的项目要进行系统拆分,就是把大系统拆分为很多小系统,几个人负责一个服务这样每个服务独立的开发、测试和上线,代码冲突少了,每次上线就回归测试自己的一个服务即可,测试速度快了,上线是独立的,只要向后兼容接口就行了,不需要跟别人等待和协调,技术架构和技术版本的升级,几个人ok就行,成本降低,更加灵活了。
什么是微服务 微服务架构(MSA)的基础是将单个应用程序开发为一组小型独立服务,这些独立服务在自己的进程中运行,独立开发和部署。如图所示:
这些服务使用轻量级 API 通过明确定义的接口进行通信。这些服务是围绕业务功能构建的,每项服务执行一项功能。由于它们是独立运行的,因此可以针对各项服务进行更新、部署和扩展,以满足对应用程序特定功能的需求。
生活中的微服务,如图所示:
程序中的微服务,就是将各个业务系统的共性再进行抽取,做成独立的服务,如图所示:
总之,微服务是分布式系统中的一种流行的架构模型,它并不是银弹,所以,也不要寄希望于微服务构架能够解决所有的问题。微服务架构主要解决的是如何快速地开发和部署我们的服务,这对于一个能够适应快速开发和成长的公司是非常必要的。同时,微服务设计中有很多很不错的想法和理念,通过学习微服务架构我们可以更快的迈向卓越。
SpringCloud Alibaba微服务解决方案 概述 Spring Cloud Alibaba 是Spring Cloud的一个子项目,致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
核心组件分析 Spring Cloud Alibaba 默认提供了如下核心功能(先了解):
服务限流降级:
默认支持 WebServlet、OpenFeign、RestTemplate、Spring Cloud Gateway, RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。服务注册与发现:
基于Spring Cloud 服务注册与发现标准,借助Nacos进行实现,默认还集成了 Ribbon 的支持。分布式配置管理:
YOLOV5s 5.0 c++调用模型onnx(超精华) 介绍训练模型.pt转onnxc++代码解析main函数部分推理部分讲解darpred部分sigmod部分结尾 介绍 现在很多开发都是需要用c++做最后一步的移植部署,手写吧,先不说你会不会浪费时间,网上找吧,问题千奇百怪,所以给大家出这篇文章,做雷锋教学,话不多说,开始
后面会贴出我的微信联系方式,有需要源码+opencv文件的自行添加我
训练模型.pt转onnx 训练部分根据呼声再决定要不要写一份博客吧!!
注意事项:
1.训练代码一定要选择yolov5 5.0版本
2. 进入models/exprort.py;
3.将红框区域换成你自己的训练完的模型
4.将版本换成12;
5.直接运行即可,会生成出onnx的模型出来。
c++代码解析 博主使用的是opencv4.5.3版本,已经编译好的,需要直接扫码加我发你 main函数部分 读取模型利用的是dnn::readNet,opencv其实挺强大的博主已经读过tf模型,torch模型后续都会出对应博客,这里总共有三点改,输入图片path,输入类别名class_names,net部分改成自己的模型
net.set这些参数都固定就好,有兴趣的同学可以研究研究DNN_TARGET_CPU这个地方,是可以使用gpu和cuda的,但是博主还没复现过
推理部分讲解 void postprocess(cv::Mat& cv_src, std::vector<cv::Mat>& outs, const std::vector<std::string>& classes, int net_size) { float confThreshold = 0.1f; float nmsThreshold = 0.1f; std::vector<int> classIds; std::vector<float> confidences; std::vector<cv::Rect> boxes; int strides[] = { 8, 16, 32 }; std::vector<std::vector<int> > anchors = { { 10,13, 16,30, 33,23 }, { 30,61, 62,45, 59,119 }, { 116,90, 156,198, 373,326 } }; for (size_t k = 0; k < outs.
参考1
参考2
1. 什么是vagrant? 简单理解,就是可以通过Vagrant这个工具管理虚拟机,比如说想创建一个centos环境的虚拟机,不需要安装系统这么麻烦,通过vagrant可以快速创建
官方网址
Vagrant是一个基于Ruby的工具,用于创建和部署虚拟化开发环境。它 使用Oracle的开源VirtualBox虚拟化系统,使用 Chef创建自动化虚拟环境。我们可以使用它来干如下这些事:
建立和删除虚拟机
配置虚拟机运行参数
管理虚拟机运行状态
自动配置和安装开发环境
打包和分发虚拟机运行环境
Vagrant的运行,需要依赖某项具体的虚拟化技术,最常见的有VirtualBox以及VMWare两款,早期,Vagrant只支持VirtualBox,后来才加入了VMWare的支持。
为什么我们要选择Vagrant呢?因为它有跨平台、可移动、自动化部署无需人工参与等优点。
在Vagrant体系中,有个box(箱子)的概念,这优点类似于docker体系中的image(镜像)。基于同一个box,不同的人可以运行得到相同的内容。这个我们下文再详细说。
2. 安装虚拟机 vitualbox 使用vagrant首先需要安装虚拟机vitualbox(virtualbox),vitualbox安装这里就不介绍了。也可以使用vmware,不过vagrant+vmware是要收费的。
2. 安装vagrant 去vagrant官网下载(vagrantup),根据系统选择安装版本,我这里选择的是windows 64位
3. 使用vagrant创建centos7 从vagrant首页“Find Boxes”进入,如下图,可以看到很多Vagrant Boxes,我现在需要的是centos7环境,找到对应centos/7 vagrant box进入
centos/7 vagrant box详情页中有使用说明,如下:
在本地创建一个目录用来存放centos7,我的目录是:D:\VirtualBox\centos7
打开powershell ,cd到centos7目录
执行命令:vagrant init centos/7 (初始化配置vagrantfile)
vagrant init centos/7 此命令会在当前目录下生成一个Vagrantfile文件
执行命令:vagrant up (启动虚拟机)
vagrant up 然后就等待下载安装咯,安装速度有点慢,毕竟是国外网络
安装成功后,可以在vitualbox中的虚拟机列表看到新安装的虚拟机
使用命令登录:vagrant ssh
vagrant ssh 当然也可以用其他第三方的连接工具。
默认登录进去的是 vagrant 用户。
所以执行有些命令的时候需要加 sudo,或者切换到 root 用户:
su root 默认 root 用户的密码为:vagrant
作者介绍 张伟伟,男,西安工程大学电子信息学院,2019级硕士研究生,张宏伟人工智能课题组。
研究方向:机器视觉与人工智能。
电子邮件:zhangweiweicpp@163.com
课题组CSDN官方账号,欢迎一键三连!. 其中的图为了节约时间,当时在拯救模式不好截图,事后总结各个帖子,解决了遇到的问题,提到的帖子当时不能解决我的问题,希望记录可以帮助到大家,不吝点赞关注和收藏哈。
我的硬件环境情况介绍:
戴尔电脑 双系统,在一块64G固态硬盘安装的windows系统,在一块500G固态安装的linux系统。
1.问题出现的状态和部分原因 无法进入系统引导项目,进入grub系统启动拯救模式,在黑窗口操作。
2.问题解决方法 先看几个指令的含义:
set 设置环境变量 ls 查看设备 insmod 加载模块 root 指定用于启动系统的分区 prefix 设定grub启动路径 2.1 问题解决方法 参考自最给力的帖子:https://www.cnblogs.com/GHzcx/p/9379871.html
ls // 第一步ls查看所有设备,启动项肯定在其中一个里 ls (hd0,gpt5) //网上的帖子根据实际情况看是gpt还是msdosX,大都是(hd0,msdosX)表示 //X代表挂载设备号1,2,3,4....查找每个设备, 直到设备内容与图片中相似,里面有一个grub/文件夹,启动项就在其中 —对各个分区,例如有s,都要运行’ls s’这样的操作,s可以为(hd0,gpt5)等,如果出现’unknown filesystem’这样的情况就跳到下一个分区.
直至——》》》
—如果明确出现了filesystem的格式(例如说ext4)不出现’unknown filesystem’。则运行: set,可以看到好多文件夹。由此尝试确定我们的引导项所在分区。
例如,我查到系统的引导项目确定在(hd0,gpt3)中,运行如下指令:
set root=(hd0,gpt3) set prefix=(hd0,gpt3)/grub //网上的帖子有的是/boot/grub,电脑不同,可参考 insmod normal normal 然后选择我们熟悉的启动项,正常进入linux操作系统
2.2 安装boot-repair软件对其进行修复 //参考自:https://www.cnblogs.com/GHzcx/p/9379871.html sudo -i //可选择进入超级管理员权限 sudo add-apt-repository ppa:yannubuntu/boot-repair && sudo apt-get update sudo apt-get install -y boot-repair && boot-repair 下载成功后弹出一个界面,选择第一项(推荐修复),waiting----
深入理解:顶点缓存对象(VBO) 顶点缓存对象(VBO) 深入理解:顶点缓存对象(VBO)前言一、创建VBO二、绘制VBO更新VBO实例源码下载引用 前言 GL_ARB_vertex_buffer_object扩展致力于提供顶点数组与显示列表的优势来提升OpenGL效率,同时避免它们实现上的不足。顶点缓存对象(VBO)准许顶点数组数据存放在服务端的高性能显卡内存中,且提供高效数据传输。如果缓存对象用于保存像素数据,就被称为像素缓存对象(PBO)。
使用顶点数组可以降低函数调用次数与降低共享顶点的重复使用。然而,顶点数组的不足之处是顶点数组函数处在客户端状态中,且每次引用都须向服务端重新发送数据。
此外,显示列表为服务端函数,因此,它并不受限于数据传输的开销。不过,一旦显示列表编译完成,显示列表中的数据不能够修改。
顶点缓存对象(VBO)在服务器端高性能内存中为顶点属性创建“缓存对象”,并且提供引用这些数组的访问函数,这些函数与顶点数组中使用的函数相同,如glVertexPointer()、glNormalPointer()、glTexCoordPointer()等。
顶点缓存对象中的内存管理根据用户提示(“target”与“Usage”模式)将缓存对象放置在最合适内存位置。因此,内存管理能够通过在系统、AGP与显卡内存三种内存之间做出平衡的方式优化缓存。
与显示列表不同,可以通过映射缓存到客户端内存空间的方式读取与更新顶点缓存对象中的数据。
VBO的另一个重要优势是就像显示列表与纹理那样可以在多个客户端共享缓存数据。因为VBO处在服务器端,多个客户端可以通过相应的标示符访问同一个缓存。
一、创建VBO 创建VBO需要3个步骤:
使用glGenBuffers()生成新缓存对象。
使用glBindBuffer()绑定缓存对象。
使用glBufferData()将顶点数据拷贝到缓存对象中。
glGenBuffers()
glGenBuffers()创建缓存对象并且返回缓存对象的标示符。它需要2个参数:第一个为需要创建的缓存数量,第二个为用于存储单一ID或多个ID的GLuint变量或数组的地址。
1
void glGenBuffers(GLsizei n, GLuint* buffers)
glBindBuffer()
当缓存对象创建之后,在使用缓存对象之前,我们需要将缓存对象连接到相应的缓存上。glBindBuffer()有2个参数:target与buffer。
1
void glBindBuffer(GLenum target, GLuint buffer)
target告诉VBO该缓存对象将保存顶点数组数据还是索引数组数据:GL_ARRAY_BUFFER或GL_ELEMENT_ARRAY。任何顶点属性,如顶点坐标、纹理坐标、法线与颜色分量数组都使用GL_ARRAY_BUFFER。用于glDraw[Range]Elements()的索引数据需要使用GL_ELEMENT_ARRAY绑定。注意,target标志帮助VBO确定缓存对象最有效的位置,如有些系统将索引保存AGP或系统内存中,将顶点保存在显卡内存中。
当第一次调用glBindBuffer(),VBO用0大小的内存缓存初始化该缓存,并且设置VBO的初始状态,如用途与访问属性。
glBufferData()
当缓存初始化之后,你可以使用glBufferData()将数据拷贝到缓存对象。
1
void glBufferData(GLenum target,GLsizeiptr size, const GLvoid* data, GLenum usage);
第一个参数target可以为GL_ARRAY_BUFFER或GL_ELEMENT_ARRAY。size为待传递数据字节数量。第三个参数为源数据数组指针,如data为NULL,则VBO仅仅预留给定数据大小的内存空间。最后一个参数usage标志位VBO的另一个性能提示,它提供缓存对象将如何使用:static、dynamic或stream、与read、copy或draw。
VBO为usage标志指定9个枚举值:
1
2
3
4
5
6
7
8
9
GL_STATIC_DRAW
GL_STATIC_READ
GL_STATIC_COPY
GL_DYNAMIC_DRAW
GL_DYNAMIC_READ
GL_DYNAMIC_COPY
GL_STREAM_DRAW
GL_STREAM_READ
GL_STREAM_COPY
”static“表示VBO中的数据将不会被改动(一次指定多次使用),”dynamic“表示数据将会被频繁改动(反复指定与使用),”stream“表示每帧数据都要改变(一次指定一次使用)。”draw“表示数据将被发送到GPU以待绘制(应用程序到GL),”read“表示数据将被客户端程序读取(GL到应用程序),”copy“表示数据可用于绘制与读取(GL到GL)。
注意,仅仅draw标志对VBO有用,copy与read标志对顶点/帧缓存对象(PBO或FBO)更有意义,如GL_STATIC_DRAW与GL_STREAM_DRAW使用显卡内存,GL_DYNAMIC使用AGP内存。_READ_相关缓存更适合在系统内存或AGP内存,因为这样数据更易访问。
glBufferSubData()
首先我们要来科普一下什么是无服务器计算,无服务器计算是在无需最终用户管理的基础设施上托管应用程序的新方式,是IaaS(基础设施即服务)演进的下一个阶段。它将底层基础架构从开发人员中分离出来,基本上虚拟化了运行时(虚拟机的一种,一般指进程级别的虚拟机)和运营管理。这通常被称为 FaaS(功能即服务)。
无服务器计算的优势表现在:敏捷、可伸缩性、计费模式、安全。其他相关知识可以百度,这里我们举个例子来说明一下:比如一款手机游戏,允许用户在不同的平台上查询全球顶级玩家分数表。当请求此信息时,请求从应用程序到API接口。API接口或许会触发某个无服务器函数,这些函数再从数据库表中获取到数据流,返回包含前五名分数的一定格式的数据。一旦构建完成,应用程序的功能就可以在基于移动和基于 web 的游戏版本中重用。这跟设置服务器不同,不是必须要有虚拟机资源或服务器资源,然后等待请求。环境由事件触发,而响应事件所需的逻辑只在响应时执行。这意味着,运行函数的资源只有在函数运行时被创建,产生一种非常高效的方法来构建应用程序。
再举个例子,比如前端调用某个API,实际是去调用后端不同语言的代码来实现相关功能,通过的做法是我们需要部署服务器,安装中间件并将程序部署到中间件中暴露接口以供调用,而无服务器的概念则是在服务器之上再抽象一层,运行时语言、中间件等环境统统封装好,你只需将调用的代码注入到对应的管道,通过触发事件来进行调用,整个程序包含非常多的代码片段及托管(可能是多种语言的),复杂的调用链事件组合,而不再需要诸如服务器部署、环境配置及维护等工作,非常之敏捷。
那么kong网关的无服务器插件究竟有哪些呢,他们运行的机制和原理是什么呢?
1、鼎鼎大名的Aws Lambda,AWS Lambda 是一项计算服务,可使您无需预配置或管理服务器即可运行代码。借助 AWS Lambda,您几乎可以为任何类型的应用程序或后端服务运行代码,并且不必进行任何管理。AWS Lambda 在可用性高的计算基础设施上运行您的代码,执行计算资源的所有管理工作,其中包括服务器和操作系统维护、容量预置和自动扩展、代码监控和记录。您只需要以 AWS Lambda 支持的一种语言提供您的代码。看一下同步调用Lambda的例子:
实际上对于kong来说,提供的插件可以替换掉上图中的Amazon API Gateway。Kong的管理平台上关于AWS Lambda的配置界面如下:
由于我没有注册AWS账户,所以没有实际测试,上面的主要参数大致讲解下:
aws key和secret:这个主要是用来鉴权和认证。
aws region:亚马逊目前在运营的几个区域,如US区,CN区,这个决定了你的function实际是放在那里。
aws function name:函数名称,你托管的代码片段函数名,kong在后端服务调用前会去调用aws上预先设置的函数。
invocation type:调用类型,包含RequestResponse(请求响应)、Event(事件)、Dryrun(空运行、预检) 。
2、微软的无服务器插件 Azure Functions,功能也亚马逊的类似,这里就不再解释。Kong网关的以上两类无服务器插件不仅可以用在全局上、还可以单独作用到服务/路由、以及单个消费方之上,而kong提供的另外两个无服务器插件Pre-function和Post-function则只能作用在全局、服务、路由上,无法作用到单个消费方上。
3、Post-function: kong提供的无服务器函数之一,提供lua的运行环境(因为kong的源码就是lua编写的)。post-function 配置好之后,服务/路由在访问时在配置的其他插件运行之后再运行post-function中配置的函数。
4、Pre-function: kong提供的无服务器函数之一,提供lua的运行环境。pre-function 配置好之后,服务/路由在访问时在配置的其他插件运行之前运行pre-function中配置的函数。这里对他进行重点验证。
网上有个例子:https://www.jianshu.com/p/3496448db28f,是使用curl以文件的方式进行配置,我没尝试成功,自己研读了kong的源码,使用kong的管理平台验证成功,以下为主要过程:
1、 kong源码中有lua的loadstring函数,对提交的function进行验证,如验证不通过,则插件配不上,loadstring函数对于分行代码需要用;来分割。
2、 假设我们要配置的源码如下:
这个源码是检查调用请求header中的x-custom-auth的值,如果为空则返回401及消息。那么按照1中的要求,写法应该是这样:
local custom_auth = kong.request.get_header(‘x-custom-auth’);if not custom_auth then;return kong.response.exit(401, “Invalid Credentials”);end;kong.service.request.clear_header(‘x-custom-auth’)
3、 通过kong管理平台配置到kong网关中:
4、 调用该API的路由,出现我们定义的消息,也就是说pre-function会在所有其他配置的插件之前运行:
5、 如果我们在请求的消息头中加上x-custom-auth,关闭base-auth和acl插件再次调用,则成功返回消息:
最后我们要提一下,为什么网关要来支持这些无服务器运营商和函数呢,第一肯定是为了网关能更加灵活的处理个性化的需求,比如在请求前和请求后做一些常用的诸如数据转换、外部调用、记录、业务计算等功能,另一方面随着serverless的大力发展,未来的趋势也是如此,当大家都在这么做的时候,设计的网关不支持serverless的调用,将会失去优势,这也是kong有先见之明之处。
目录
简介
优势: 劣势:
语法
message常见的数据类型与go中类型对比
举例
关键字
编译
简介 Protobuf是Google公司开发的一种数据描述语言,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。适合做数据存储或 RPC 数据交换格式。
protobuf是类似与json一样的数据描述语言(数据格式)protobuf非常适合于RPC数据交换格式 优势: 序列化后体积相比Json和XML很小,适合网络传输 支持跨平台多语言 消息格式升级和兼容性还不错 序列化反序列化速度很快,快于Json的处理速度 劣势: 应用不够广(相比xml和json)二进制格式导致可读性差缺乏自描述 语法 message常见的数据类型与go中类型对比 .proto类型Go类型介绍doublefloat6464位浮点数floatfloat3232位浮点数int32int32使用可变长度编码。编码负数效率低下——如果你的字段可能有负值,请改用sint32。int64int64使用可变长度编码。编码负数效率低下——如果你的字段可能有负值,请改用sint64。uint32uint32使用可变长度编码。uint64uint64使用可变长度编码。sint32int32使用可变长度编码。符号整型值。这些比常规int32s编码负数更有效。sint64int64使用可变长度编码。符号整型值。这些比常规int64s编码负数更有效。fixed32uint32总是四字节。如果值通常大于228,则比uint 32更有效fixed64uint64总是八字节。如果值通常大于256,则比uint64更有效sfixed32int32总是四字节。sfixed64int64总是八字节。boolbool布尔类型stringstring字符串必须始终包含UTF - 8编码或7位ASCII文本bytes[]byte可以包含任意字节序列 举例 myproto.proto
// 默认是proto2 syntax = "proto3";//正在使用`proto3`语法 // 指定所在包名 package PB; option go_package = "../pb";// 避免错误 unable to determine Go import path for "myproto.proto" // 定义枚举类型 enum Week{ Monday = 0;//peoto3中,枚举值必须是从0开始,不能设置成1开始 Tuesday = 1; } // 定义消息体 message Student{ int32 age = 1;//可以不从1开始,但是不能重复。19000-19999范围的数字不能用,系统保留。 string name = 2; People p = 3; repeated int32 score = 4;//数组 // 枚举 Week w = 5; // 联合体 oneof data{ string teacher = 6;//不能写5 或者 3,联合体是只能取其中的一个,编号唯一,不能重复 string class = 7; } } // 消息体支持嵌套 message People{ int32 weight = 1; } // 添加 rpc 服务 // 编译期间不编译服务, // 使用 protoc --go_out=plugins=grpc:.
相差为2的两个素数称为孪生素数。例如,3与5,41与43等都是孪生素数。设计程序求出指定区间上的所有孪生素数对。区间上限和下限由键盘获取。 要求:判断一个数是否为素数必须定义函数实现。
提示:在主函数对第一个数穷举,然后+2得到第二个数,再调用函数判断两个是否都是素数。
**输入提示信息:"Please input begin and end(begin>2):" **输入格式要求:"%ld,%ld"
**输出格式要求:"(%ld,%ld)"
程序运行示例如下:
Please input begin and end(begin>2):10,100
(11,13)(17,19)(29,31)(41,43)(59,61)(71,73)
哎,随便看看,互帮互助嘛
#include <stdio.h> #include <math.h> int Prime(int n);//定义函数,判断是否为素数。 int main() { long begin, end, i, j;//定义变量 printf("Please input begin and end(begin>2):"); scanf("%ld,%ld", &begin, &end); for (i = begin; i <= end-2; i++) { if (Prime(i)&&Prime(i+2)) //调用Prime()函数, { printf("(%ld,%ld)", i, i+2); } } return 0; } int Prime(int n) { int i; for ( i = 2; i <= sqrt(n); i++) //若数为素数,则存在一个因数i属于(2,sqrt(n)) { if (n % i == 0) { return 0; //若不是素数,返回0 ; } } return 1;//是素数,返回1; }
我们在建站的时候,通常为了节省备案时间,都喜欢选择免备案海外、国外服务器,但选择免备案服务器有一个缺陷,就是距离远、国内用户的访问速度慢,怎么解决国外服务器在国内访问速度慢的问题?提高国外服务器速度的方法又有哪些?下面我们就聊聊国外服务器怎么加速和有哪些加速方法。
国外服务器怎么加速?提高国外服务器速度的方法
在国内使用免备案海外、国外服务器的用户为了保证国内搜索引擎的关键词排名、网站优化效果,通常会给服务器做加速处理,而提高国外服务器的国内访问速度方法则有以下几点:
1、CDN节点加速:CDN加速这种是我们常见的加速方法之一,利用网站缓存至距离用户近的节点服务器,这种方法有个缺点,就是操作不当容易引起网站打不开或者个别地区无法访问等情况,难以排查,严重的甚至是降权、流量下降等情况,不过也是对提高国外服务器速度较为有效的方法。
2、选择CN2线路:CN2线路是海外、国外服务器访问国内的专用直连线路,相比传统的国外服务器线路,CN2线路能够大幅度提高国外服务器速度,所以在租用国外服务器时,建议租用拥有CN2线路的国外服务器,比如像HenghHost海外、国外服务器都默认使用CN2线路,并且移动、联通线路经过CN2优化过,相比传统的三网直连效果要好很多。
3、增加服务器资源:增加服务器资源也可以有效的提高服务器速度,比如带宽,网站运行时间太久,会占用和消耗大量的服务器资源,致使服务器资源不足,从而影响了服务器效率,不过这种方法比较适合内容体量、流量较大的网站。
4、优化服务器:这种方法与第三点原理相同,都是由于网站占用太多的服务器资源配置,导致网站运行缓慢、加载速度慢等,通过优化占用服务器资源配置的相关文件,比如图片大小、视频、缓存、请求过多等等,降低占用率,提升运行效率。
总结:国外服务器怎么加速?通过上面几点可以有效的提高国外服务器速度慢的问题,国内访问速度慢是所有国外服务器的通病,要想进一步提高海外免备案服务器的速度较好的选择就是香港服务器。
int binary_search(int[] nums, int target) { int left = 0, right = nums.length - 1; while(left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if(nums[mid] == target) { // 直接返回 return mid; } } // 直接返回 return -1; } int left_bound(int[] nums, int target) { int left = 0, right = nums.
Room 在SQLite上提供了一个抽象层,以便在充分利用SQLite的强大功能的同时,能够流畅地访问数据库。
Android终端立即断电重启时,SQ写入磁盘会有延时,导致数据丢失。
Linux/Unix系统中,在文件或数据处理过程中一般先放到内存缓冲区中,等到适当的时候再写入磁盘,以提高系统的运行效率。
在插入/更新数据库后,获取数据库地址File句柄,刷新一下。
public void DBSync() throws IOException { String path = mDataBase.getOpenHelper().getWritableDatabase().getPath(); FileOutputStream fos = null; try { fos = new FileOutputStream(path); fos.flush(); //将数据同步到达物理存储设备 FileDescriptor fd = fos.getFD(); fd.sync(); } catch(Exception e) { e.printStackTrace(); } finally { if(fos != null) fos.close(); } }
在开发一个模拟人工自动网银转账的软件的时候,大量使用了async+await,在项目成功上线并且迭代了若干版本后,对于await相关的代码进行了重构,目前的结构更清晰,可扩展性更强。同时对于await的使用也从很初级,变得有一些经验,现在把这些经验总结如下:
1 await和async配对使用。最典型的应用如下:
下面的代码是不用线程的情况,winform就会在5秒的无响应状态后才会在testbox中显示“test”,用户体验不好。
private string testString()
{
Thread.Sleep(5000);
return "test";
}
private async void btnGetAndTransfer_Click(object sender, EventArgs e)
{
tbTextBox.text=testString();
}
下面的代码使用传统的threading技术更新textbox的内容,非常复杂,代码难于维护
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
new Thread(SampleFunction).Start();
}
public void SetTextBox(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(SetTextBox), new object[] {value});
return;
}
textBox1.Text = value;
}
void SampleFunction()
{
Thread.Sleep(5000);
SetTextBox("test");
}
}
下面的代码使用await+async实现了第一段代码的功能,并且解决了winform冻结的问题。从代码的结构来说,也是清晰易读。 private async Task<string> testString()
一直以来有这样的疑惑,在现如今多核多线程的电脑处理器之下,一个进程中的几个线程是 怎么运行的呢?(是经系统和JVM分配少量的资源 最后轮流切换 时间调度?还是这几个线程分配到不同的核上同时运行?)
今天就这一问题查了一些资料,现整理如下:
单个CPU一个时刻只能运行一个线程?
单核CPU电脑同一时间内只能执行一个线程,首先了解一下,CPU执行的过程 ,它是把时间分成若干个小时间段,这些时间段很小的,系统中有很多进程,每个进程中又包含很多线程,在同一 时间段 内,电脑CPU只能处理一个线程(线程A),而下一个 时间段 就不一定是上一个时间段内执行的那个线程(线程A)了,可能是别的线程(线程B 吧)
CPU采用的是类似于时间片轮转的机制,也就是说同一时间一条进程提出执行请求时,其他进程只能等待它执行完毕,CPU才会处理其他请求。其他进程相当于在排队等待中。当然了,为了避免某条进程无限制时间的执行,一般会限定一个时间,超时 的话,CPU根据一定的线程调度算法来切换线程。可以看做很多线程在并发执行。其实还是在某一个时间点上只有一个线程在运行罢了。
多核的话,每个核心都是同样的原理。但是两个核心就可以通过系统分配资源,同时执行不同的进程,这个就更复杂了。
每条进程都有CPU分配的进程号的。避免混乱。
一个核心就是实实在在的一个cpu处理设备 线程的概念可以理解成电脑处理信息的通道 既一个线程一个通道 一般来说一个cpu核心处理一个通道的信息 但也不是绝对 因特尔支持超线程技术的cpu每个核心可以处理两个或多个通道的信息 这就可以形容为超线程(既多出来的通道的处理能力)但前提是软件也必须的支持超线程才行 否则单核双线程或多线程也只能有单个通道工作 从某种意义上来说cpu的能力被浪费了 所以网友一般就会说 真正的核心数(通道) 比虚拟出来的核心(通道)来个更实在。最后 线程数决定这CPU能同时处理几件事情,在没有超线程技术的情况下核心数等於线程数。
java线程调度
CPU对于各个线程的调度是随机的(分时调度),在Java程序中,JVM负责线程的调度。 线程调度是指按照特定的机制为多个线程分配CPU的使用权,也就是实际执行的时候是线程,因此CPU调度的最小单位是线程,而资源分配的最小单位是进程。
JVM调度的模式有两种:分时调度和抢占式调度。
分时调度 是所有线程轮流获得CPU使用权,并平均分配每个线程占用CPU的时间;
抢占式调度 是根据线程的优先级别来获取CPU的使用权。JVM的线程调度模式采用了抢占式模式。既然是抢占调度,那么我们就能通过设置优先级来“有限”的控制线程的运行顺序,注意“有限”一次。
CPU核数 跟多线程 的关系
要说多线程就离不开进程,进程和线程的区别在这里就不详细说了,只将关键的几点:
a)进程之间是 相互独立的,不共享 内存和数据,线程之间 的内存和数据是 公用的,每个线程只有自己的一组CPU指令、寄存器和堆栈,对于线程来说只有CPU里的东西是自己独享的,程序中的其他东西都是跟同一个进程里的其他线程共享的。
b)操作系统创建进程时要分配好多外部资源,所以开销大。(这个跟操作系统有关,有人做过实验,window创建进程的开销大,Linux创建进程的开销就很小。)
再来说一下CPU,过去单CPU时代,最先是单任务阶段 在一个时间点 只能执行单一程序。之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程。虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行。而现在多核CPU的情况下,同一时间点可以执行多个任务(并行),具体到这个任务在CPU哪个核上运行,这个就跟操作系统和CPU本身的设计相关了。
我们假设一个极端的情况:在一台单核计算机上只运行2个程序,一个是我们的程序A,另一个是操作系统的程序B,每个程序是一个进程。单核CPU的时候,A和B在CPU上交替运行,具体的分配方式由操作系统来判断,我这里猜测应该跟A和B的线程数有关,因为线程是CPU级别的,如果A有5个线程,B也有5个线程,那么CPU分配给A和B的时间应该是1:1的;如果A增加到15个线程,CPU分配给A和B的时间应该是3:1的比例。所以此时如果A的线程数多,那么获得的CPU执行次数就多,处理的速度也就快了。以上假设的前提是:①A和B的优先级相同,②A和B都是只消耗CPU资源的程序。
如果相同的情况用一个双核的计算机来处理又会是什么结果呢?假设这个双核的计算机和操作系统比较傻,把A进程分配到核1上,B进程分配到核2上,那不管A有几个线程,都是用核1来处理,那么时间肯定是一样的。不过现实中应该不会有这么傻的CPU和操作系统吧。所以赶紧还是会根据线程来进行处理,当A的线程比B多时,会占用核2来处理A的线程。
刚才说的是只消耗CPU资源的程序,但这样的程序在实际应用中基本上是没有的,总会有跟资源打交道的。比如读个文件,查个数据库,访问一个网络连接等等。这个时候多线程才真正体现出优势,在一个进程中,线程a去读文件,线程b去查数据库,线程c去访问网络,a先用一下CPU,然后去读文件,此时CPU空闲,b就用一下,然后去查数据库,……,相对于读文件、查数据库、访问网络来说CPU计算的时间几乎可以忽略不计,所以多线程实际上是计算机多种资源的并行运用,跟CPU有几个核心是没什么关系的。
https://my.oschina.net/xiaotian120/blog/196201 (java线程调度)
http://blog.csdn.net/ziwen00/article/details/38097297(这篇写的也不错)
原文链接:https://blog.csdn.net/qq_33530388/article/details/62448212
在学习黑马的课程时发现使用的是JDK8,而我自己安装的是JDK16,发生了版本不匹配的类加载错误。 提醒自己以后在看报错时除了看错误类型,还要格外注意Caused by之后的错误原因
看了很多文章,都说把JDK16换成JDK8就好了,那么既然可以降低JDK的版本,我也可以尝试着提升Spring-context的版本。Spring5.0.5会抛出异常:
java.lang.IllegalStateException: Cannot load configuration class
再去maven坐标官网查询相关的依赖版本,得到:
所以教程中使用的5.0.5相对而言已经比较旧了,尝试使用5.2.19发现依然报错,
Caused by: org.springframework.core.NestedIOException: ASM ClassReader failed to parse class file - probably due to a new Java class file version that isn’t supported yet
到这里可以确定是Spring和JDK版本的不兼容引发的错误,再次换成Spring5.3.13,成功运行,按.xml文件配置依赖注入的相同结果:
总结:最好下载教程对应的资料进行配套学习,版本可能引发许多问题。
为什么要学习算法? 在有限时间和内存下,编写程序正确地或有效地解决问题。
计算Fibonacci数列的第n项 算法一:递归函数 int F(int n){ if(n<=2) return 1; return F(n-1)+F(n-2); } 算法二:递推循环 int F(int n){ if(n<=2) return 1; int f1=1, f2=1; for(int i=3;i<=n;i++){ int temp = f1+f2; f1=f2; f2=temp; } return f2; } 递归函数计算100项时,会无法完成计算,递推可以很快计算出100项。
递归算法的时间复杂度为O(1.618^n),递推函数的时间复杂度为O(n)。
算法三:矩阵快速幂算法 数列递推公式的矩阵形式,数学通项公式的矩阵形式
即使有了通项公式,要计算n-2次幂还是需要O(n)时间
快速幂算法 每次迭代问题规模减半,时间复杂度O(log n)
class Matrix{ Matrix operator*(const Matrix &); // ... }; Matrix Quickpow(Matrix A, int n){ if(n==1) return A; Matrix half = Quickpow(A, n/2); if(n%2==1){ return A*half*half; }else{ return half*half; } } int F(int n){ if(n<=2) return 1; Matric A(1,1,1,0); A = Quickpow(A,n-2); return A[0][0] + A[0][1]; } 当数据规模更大时,O(log n)能比O(n)快得更多
文章目录 1、配置实例1.1、配置实例(代理实例一)1.2、配置实例(代理实例二)1.3、配置实例(负载均衡)1.4、配置实例(动静分离)1.4.1、相关概念1.4.2、配置步骤 2、nginx原理解析2.1、master和worker2.2、worker是如何工作的?2.3、一个master和多个worker的好处2.4、设置多少个worker才最合适?2.5、连接数 worker_connection 1、配置实例 1.1、配置实例(代理实例一) 1. 实现效果
打开浏览器,在浏览器地址栏输入地址 www.123.com,跳转到linux系统tomcat主页。 2. 准备工作
(1)、在linux系统安装tomcat,使用默认端口 8080。
tomcat安装文件放到linux系统中的 /usr/src 目录下,解压。进入tomcat的bin目录中,./startup.sh 启动tomcat服务器。 (2)、对外开放的端口
# 查看开放的端口号 firewall-cmd --list-all # 设置开放的端口号 firewall-cmd --add-service=http --permanent sudo firewall-cmd --add-port=8001/top --permanent # 重启防火墙 firewall-cmd --reload (3)、在windows系统中通过浏览器访问tomcat服务器。
3. 访问过程分析
它会首先查看本机中是否有相应的ip地址的映射,如果没有的话,才会将请求发送给DNS进行域名解析。 4. 具体配置
第一步:在windows系统的host文件进行域名和ip地址对应关系的配置。
目录为 C:\Windows\System32\drivers\etc。需要添加的内容为 192.168.123.129 www.123.com。 第二步:在nginx进行请求转发的配置(代理配置)
如果我们请求的是 192.168.123.129:80 的话,它会把我们的请求转发到 http://127.0.0.1:8080。 5. 最终测试
可以看到我们请求的是www.123.com,它会帮我们跳转到 192.168.123.129:8080。如果满足上述所说,那么我们就配置成功了。 1.2、配置实例(代理实例二) 1. 实现效果
使用nginx进行代理,根据访问的路劲跳转到不同端口的服务中。nginx监听端口为9001。访问 http://192.168.123.129:9001/edu/直接跳转到127.0.0.1:8080。访问 http://192.168.123.129:9001/vod/ 直接跳转到127.0.0.1:8081。 2. 准备工作
(1)、准备两个tomcat服务器,一个8080端口,一个8081端口。
(2)、创建文件夹和测试页面。项目默认部署在webapps文件夹下
3. 具体配置
1.概述 Android中触摸事件传递过程中最重要的是dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()方法;有很多工作几年的或者初学者总是感到困惑的问题之一,困惑的问题主要就是事件的传递机制和响应机制;今天我们就整理一下dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()的处理过程,方便理解;
dispatchTouchEvent()是处理触摸事件分发,事件(多数情况)是从Activity的dispatchTouchEvent()开发的;执行getWindow().superDispatchTouchEvent(ev)事件向下分发; Activity public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } //调用Window下的superDispatchTouchEvent()方法 if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); } PhoneWindow @Override public boolean superDispatchTouchEvent(MotionEvent event) { //调用Window窗口下的DecorView的superDispatchTouchEvent()方法 return mDecor.superDispatchTouchEvent(event); } DecorView public boolean superDispatchTouchEvent(MotionEvent event) { //调用DecorView父视图的super.dispatchTouchEvent()方法 return super.dispatchTouchEvent(event); } ViewGroup public boolean dispatchTouchEvent(MotionEvent event) { ... handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); ... return handled; } private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; .
按 Press功能 FunctionCtrl + Shift + P,F1显示命令面板 Show Command PaletteCtrl + P快速打开 Quick OpenCtrl + Shift + N新窗口/实例 New window/instanceCtrl + Shift + W关闭窗口/实例 Close window/instance 基础编辑 Basic editing 按 Press功能 FunctionCtrl+X剪切行(空选定) Cut line (empty selection)Ctrl+C复制行(空选定)Copy line (empty selection)Alt+ ↑ / ↓向上/向下移动行 Move line up/downShift+Alt + ↓ / ↑向上/向下复制行 Copy line up/downCtrl+Shift+K删除行 Delete lineCtrl+Enter在下面插入行 Insert line belowCtrl+Shift+Enter在上面插入行 Insert line aboveCtrl+Shift+\跳到匹配的括号 Jump to matching bracketCtrl+] / [缩进/缩进行 Indent/outdent lineHome转到行首 Go to beginning of lineEnd转到行尾 Go to end of lineCtrl+Home转到文件开头 Go to beginning of fileCtrl+End转到文件末尾 Go to end of fileCtrl+↑ / ↓向上/向下滚动行 Scroll line up/downAlt+PgUp / PgDown向上/向下滚动页面 Scroll page up/downCtrl+Shift+[折叠(折叠)区域 Fold (collapse) regionCtrl+Shift+]展开(未折叠)区域 Unfold (uncollapse) regionCtrl+K Ctrl+[折叠(未折叠)所有子区域 Fold (collapse) all subregionsCtrl+K Ctrl+]展开(未折叠)所有子区域 Unfold (uncollapse) all subregionsCtrl+K Ctrl+0折叠(折叠)所有区域 Fold (collapse) all regionsCtrl+K Ctrl+J展开(未折叠)所有区域 Unfold (uncollapse) all regionsCtrl+K Ctrl+C添加行注释 Add line commentCtrl+K Ctrl+U删除行注释 Remove line commentCtrl+/切换行注释 Toggle line commentShift+Alt+A切换块注释 Toggle block commentAlt+Z切换换行 Toggle word wrap 导航 Navigation 按 Press功能 FunctionCtrl + T显示所有符号 Show all SymbolsCtrl + G转到行.
目录
一、signal类
二、基本用法
1.成员函数
2.返回值
3.使用组号
4.合并器
(1)使用signal默认构造函数
(2)实例化合并器
5.管理信号连接
(1)使用connection对象管理信号连接
(2)使用scope_connection对象管理信号连接
(3)使⽤slot类自动管理连接
三、线程安全
四、与function对比
在signals2库中,观察者模式被称为信号/插槽(signals/slots)机制,它是⼀种函数回调机制,⼀个信号关联了多个插槽,当信号发出时,所有关联它的插槽都会被调⽤。 许多成熟的软件系统都⽤到了这种信号/插槽机制(另⼀个常⽤的名称是事件处理机制:event/ event handler),它可以很好地解耦⼀组互相协作的类,有的语⾔甚⾄直接内建了对它的⽀持, signals2以库的形式为C++增加了这个重要的功能。
一、signal类 signals2库的核⼼是signal类,相当于C#语⾔中的event+delegate。
signal的模板参数列表相当⻓,总共有7个参数,这⾥仅列出了⽐较重要的4个,⽽且除了第⼀个 是必需的外,其他的都可以使⽤默认值。
template< typename Signature, typename Combiner = optional_last_value<typename boost::function_traits<Signature>::result_type>, typename Group = int, typename GroupCompare = std::less<Group>, typename SlotFunction = function<Signature>, typename ExtendedSlotFunction = typename detail::extended_signature<function_traits<Signature>::arity, Signature>::function_type, typename Mutex = mutex > 第⼀个模板参数Signature的含义与function的含义相同,它也是⼀个函数类型,表示可被 signal调⽤的函数(插槽、事件处理handler)。第⼆个模板参数Combiner是⼀个函数对象,它被称为“合并器”,⽤来组合所有插槽的调⽤结 果,默认是optional_last_value<R>,它使⽤optional库返回最后⼀个被调⽤的插槽的返回值。第三个模板参数Group是插槽编组的类型,默认使⽤int来标记组号,也可以改为std::string 等类型,但通常没有必要。第四个模板参数GroupCompare与Group配合使⽤,⽤来确定编组的排序准则,默认是升序 (std::less<Group>),因此要求Group必须定义operator<。 signal继承⾃signal_base,⽽signal_base⼜继承⾃noncopyable,因此signal是不可拷⻉的。如果把signal作为⾃定义类的成员变量,那么⾃定义类也将是不可拷⻉的,除⾮使⽤ shared_ptr/ref来间接持有它。
二、基本用法 1.成员函数 connect():signal最重要的操作函数是插槽管理函数connect(),它把插槽连接到信号上,相当于为信号 (事件)增加了⼀个处理的handler。第⼆个参数connect_position,它的默认值是at_back, 表示将插槽插⼊信号插槽链表的尾部。
插槽可以是任意的可调⽤对象,包括函数指针、函数对象,以及它们的bind/lambda表达式和 function对象,signal内部使⽤function作为容器来保存这些可调⽤对象。 连接时可以指定组号也可以不指定组号,当信号发⽣时将依据组号的排序准则依次调⽤插槽函数。如果连接成功,connect()将返回⼀个connection对象,表示信号与插槽之间的连接关系,它是⼀个轻量级对象,可以处理两者间的连接,如断开、重连接或测试连接状态。
实现功能:
固定一个高度,超出该高度的部分就隐藏,并且显示滚动条能上拉下滑滚动
实现代码:
height: 500rpx; overflow-x: hidden; overflow-y: scroll; 实现功能:
固定一个宽度,超出该宽度的部分就隐藏,并且显示滚动条能左右滚动
实现代码:
width: 500rpx; overflow-x: scroll; overflow-y: hidden; 对你有帮助的话点个赞吧~
53. 最大子数组和 给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的 分治法 求解。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-subarray/
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
int maxSubArray(int* nums, int numsSize){ int subsum = 0, maxnumsum = nums[0]; for (int i = 0; i < numsSize; i++){ if (subsum > 0){ subsum += nums[i]; }else{ subsum = nums[i]; } if (subsum > maxnumsum){ maxnumsum = subsum; } } return maxnumsum; }
YOLO中的NMS 对于每一个种类的概率,比如Dog,我们将所有98个框按照预测概率从高到低排序(为方便计算,排序前可以剔除极小概率的框,也就是把它们的概率置为0),然后通过非极大抑制NMS方法,继续剔除多余的框:
NMS方法在这里如何运行呢?
1)首先因为经过了排序,所以第一个框是概率最大的框(下图橘色)。然后继续扫描下一个框跟第一个框,判断是否IOU大于0.5:
如果IOU大于0.5,那么第二个框是多余的,将它剔除:
继续扫描到第三个框,它与最大概率框的IOU小于0.5,保留:
继续扫描到第四个框,同理需要保留:
继续扫描后面的框,直到所有框都与第一个框比较完毕。 2)接下来,以次大概率的框(因为一开始排序过,它在顺序上也一定是保留框中最靠近上一轮的基础框的)为基础,将它后面的其它框与之比较。。
即第三个bb15与其后面的bbox进行IoU比较,重复上面1)的比较步骤
=
若IOU大于0.5,所以可以剔除第4个框:
3)重复步骤2),每一个非0的bbox与其后面的bbox进行比较,直到只剩最后一个非0的bbox,结束。
假设上面的bbox对于Dog类别经历了所有的扫描之后,只留下了两个框:
这时候,或许会有疑问:明显留下来的蓝色框,并非Dog,为什么要留下?因为对计算机来说,图片可能出现两只Dog,保留概率不为0的框是安全的。不过的确后续设置了一定的阈值(比如0.3)来删除掉概率太低的框,这里的蓝色框在最后并没有保留,因为它在20种类别里要么因为IOU不够而被删除,要么因为最后阈值不够而被剔除。
4)上面描述了对Dog种类进行的框选择。接下来,继续对其它19种类别分别进行上面的操作。
即每一行都组成一个集合进行上述1)、2)、3)的操作。
==5)最后进行纵向跨类的比较(为什么?因为上面就算保留了橘色框为最大概率的Dog框,但该框可能在Cat的类别也为概率最大且比Dog的概率更大,那么我们最终要判断该框为Cat而不是Dog)。判定流程和法则如下:
注意:
NMS是对所有的类别分别执行的。
举个栗子,假设最后预测出的矩形框有2类(分别为cup, pen),在NMS之前,每个类别可能都会有不只一个bbx被预测出来,这个时候我们需要对这两个类别分别执行一次NMS过程。
1、问题:Excel如何将某列文本内容符合要求的转移到另一列中? 2、方法:使用多个函数组合 =IF(ISERROR(FIND("@",B2)),A1,B2) 解读,使用FIND查找自己所需要的文本,此处需要的是以@开始的字符,查找到的位置是B2,如果满足条件,则把B2的值写在公式所在的单元格(A1),如果不满足,则把A1的单元格的内容写在公式所在的单元格。A1和B2的单元格的数字会随着公式所在的单元格变动而变动。
加密算法 超级加解密转换工具
常见加密编码等算法解析 MD5 SHA ASC进制 时间戳 URL BASE64 Unescape AES DES
MD5
密文一般是0-9,a-f,为不可逆解密,只能从明文知道密文,cmd5网站是枚举的方法由密文知道明文,是先将所有的明文出现的可能的密文保存起来再一一对应
SHA
与MD5相似
ASC进制
类似于二进制,十进制,十六进制
时间戳
一些脚本或数据库记录的时间与平时读的时间不一样
URL编码
有% 0-9 a-z,例如and 的url编码是%20
base64
0-9 a-z 还有=,字母会出现大小写的情况,有些会在后面一个或两个=,出现明文长度等于密文
unescape
与url编码相似,一般是一个%u+数字
AES
密文与base64类似,如果用base64解密依旧乱码的话说明加密的方法可能是AES,要知道密码,偏移量才能进行加密或解密
DES
有密码和加密两层功能
常见加密形式算法解析 直接加密,带salt ,带密码,带偏移,带位数,带模式,带干扰,自定义组合
常见解密方式 枚举,自定义逆向算法,可逆向
了解常规加密算法的特性 长度位数,字符规律,代码分析,搜素获取