标签目录:摩登注册首页

摩登3测速代理_为什么要阻抗匹配?怎么进行阻抗匹配?

1 什么是阻抗 在电学中,常把对电路中电流所起的阻碍作用叫做阻抗。阻抗单位为欧姆,常用Z表示,是一个复数Z= R+i( ωL–1/(ωC)) 具体说来阻抗可分为两个部分,电阻(实部)和电抗(虚部)。 其中电抗又包括容抗和感抗,由电容引起的电流阻碍称为容抗,由电感引起的电流阻碍称为感抗。 2 阻抗匹配的理想模型 射频工程师大都遇到过匹配阻抗的问题,通俗的讲,阻抗匹配的目的是确保能实现信号或能量从“信号源”到“负载”的有效传送 其最最理想模型当然是希望Source端的输出阻抗为50欧姆,传输线的阻抗为50欧姆,Load端的输入阻抗也是50欧姆,一路50欧姆下去,这是最理想的。 然而实际情况是:源端阻抗不会是50ohm,负载端阻抗也不会是50ohm,这个时候就需要若干个阻抗匹配电路 而匹配电路就是由电感和电容所构成,这个时候我们就需要使用电容和电感来进行阻抗匹配电路调试,以达到RF性能最优。 3  阻抗匹配的方法 阻抗匹配的方法主要有两个,一是改变阻抗力,二是调整传输线。 改变阻抗力就是通过电容、电感与负载的串并联调整负载阻抗值,以达到源和负载阻抗匹配。 调整传输线是加长源和负载间的距离,配合电容和电感把阻抗力调整为零。 此时信号不会发生发射,能量都能被负载吸收。 高速PCB布线中,一般把数字信号的走线阻抗设计为50欧姆。一般规定同轴电缆基带50欧姆,频带75欧姆,对绞线(差分)为85-100欧姆。 4 Smith圆图在RF匹配电路调试中的应用 Smith圆图上可以反映出如下信息: 阻抗参数Z,导纳参数Y,品质因子Q,反射系数,驻波系数,噪声系数,增益,稳定因子,功率,效率,频率信息等抗等参数。 是不是一脸懵,我们还是来看阻抗圆图吧: 阻抗圆图的构图原理是利用输入阻抗与电压反射系数之间的一一对应关系,将归一化输入阻抗表示在反射系数极坐标系中,其特点归纳如下: 1.上半圆阻抗为感抗,下半圆阻抗为容抗; 2.实轴为纯电阻,单位圆为纯电抗; 3.实轴的右半轴皆为电压波腹点(除开路点),左半轴皆为电压波节点(除短路点); 4.匹配点(1,0),开路点(∞,∞)和短路点(0,0); 5.两个特殊圆:最大的为纯电抗圆,与虚轴相切的为匹配圆; 6.两个旋转方向:逆时针转为向负载移动,顺时针转为向波源移动。 导纳圆图与阻抗圆图互为中心对称,同一张圆图,即可以当作阻抗圆图来用,也可以当作导纳圆图来用,但是在进行每一次操作时,若作为阻抗圆图用则不能作为导纳圆图。 Smith圆图中,能表示出一些很有意思的特征: 在负载之前串联或并联一个可变电感/电容,电路图如下图左侧4个图所示,将得到Smith圆图上右侧的几条曲线。对应Smith阻抗圆及导纳圆,其运动轨迹如下: 1、使用Smith阻抗圆时,串联电感顺时针转,串联电容逆时针转; 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登三1960_基于STM32F103自制CMSIS-DAP下载器

关注+星标公众号,不错过精彩内容 编排 | strongerHuang 微信公众号 | strongerHuang 市面上针对Cortex-M处理器的下载器,有很多是基于CMSIS-DAP演变而来,比如:e-Link、GD-Link等。 之前给大家分享过自制ST-Link的教程,今天继续为大家分享一篇:基于STM32F103C8,自制CMSIS-DAP下载器。 1 关于CMSIS-DAP CMSIS-DAP是支持访问 CoreSight 调试访问端口(DAP)的固件规范和实现,以及各种Cortex处理器提供CoreSight调试和跟踪。 地址: https://arm-software.github.io/CMSIS_5/DAP/html/index.html CMSIS-DAP固件作为源代码提供,并且可以完全配置为新的调试单元。 这里相关的更多内容,可以参看我之前分享过的一篇文章:Cortex-M软件接口标准CMSIS那些重要内容。 2 CMSIS-DAP固件 CMSIS-DAP固件Arm以源码形式提供,不存在版权问题(因为针对Arm Cortex处理器,他们还希望更多人使用)。 1.固件版本 目前有两个版本: 版本1配置使用USB HID作为与主机PC的接口。 版本2配置使用WinUSB作为与主机PC的接口,并提供高速SWO跟踪流。 2.源码位置 目前源码提供在Keil MDK V5版本,安装好Keil MDK,你在安装目录下就能找到源码。 C:\Keil_v5\ARM\Packs\ARM\CMSIS\5.7.0\CMSIS\DAP\Firmware (目前MDK V5.33,CMSIS版本为5.7.0) 3.源码描述 从文件目录可以看出,官方源码提供了一些模板和例子。 目前只提供了LPC处理器的例子,如果你有这个处理器对应的板卡,可以直接使用该源码做一个下载调试器。(下面就针对于LPC这个例子进行“改装”) 3 配置 利用STM32CubeMX图形化配置工具,帮助用户选择单片机引脚的功能,并自动生成外设初始化代码。配置了USB、SPI1和USART1,并选择了USB的Custom HID middleware模式。GPIOB10到GPIO15被配置为JTAG调试需要的引脚。GPIOC13用于驱动单片机上的LED灯。 ST公司也开发了他们自己的JTAG调试器——STLink。当然它并不是必要的,你也可以使用J-Link或者其他种类的调试器。STLink的驱动和程序可以在ST官网上下载。在网站里还有一个基于Eclipse开发环境开发的IDE,STM32CubeMX也被包含其中。我选择的IDE是基于CodeBlocks的Embitz,IDE中的arm_none_eabi_gcc版本是5.4.1。在我完成我的CMSIS-DAP之前,我必须使用STLink来调试我的代码。现在我在使用新做出来的CMSIS-DAP结合OpenOCD进行日常的开发。 4 从CMSIS-DAP的源码开始 源码可以在官网下载: https://github.com/ARM-software/CMSIS_5 也可以直接在 Keil MDK 安装目录下获取: C:\Keil_v5\ARM\Packs\ARM\CMSIS\5.7.0\CMSIS\DAP 将从示例V1的头文件 DAP_config.h 开始分析。 选择LPC-Link-II V1作为我的参考是因为它是通过USB HID实现的(V2是通过WinUSB实现)。我分析的第一个文件是DAP_Config.h。第一个关键位置如下: #ifdef _RTE_#include "RTE_Components.h"#include CMSIS_device_header#else#include "device.h" #endif 不用RTE的相关文件,创建我自己的device.h。 我将参数CPU_CLOCK重定义为72000000(72MHz)。根据文件里的注释,参数DAP_PACKET_SIZE必须重新定义为64U。我把SWO_UART改为0,这让我的工作轻松不少。参数TIMESTAMP_CLOCK也要重定义为72000000。LPC-Link-II使用Cortex-M3 的 DWT模块实现时间戳(TIMESTAMP),这也是为什么我想在CubeMX中尝试配置STM32F103的DWT。最后我自己写了一小段代码来实现这个功能(在device.c中): CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;/** * On Cortex-M7 core there is a LAR register in DWT domain. * Any time we need to setup DWT registers, we MUST write * 0xC5ACCE55 into LAR first. LAR means Lock Access Register. */DWT->CYCCNT = 0;DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; 我定义的引脚和NXP LPCxx的完全不同。我为STM32F103重写了所有的引脚的操作代码。在DAP_Config.h这个文件中还有一些奇怪的地方,比如: // SWCLK/TCK I/O pin ------------------------------------- /** SWCLK/TCK I/O pin: Get Input.\return Current…

摩登3测速登陆_格力电器推出大松5G手机,格力手机未来还有戏吗?

许久没有消息的格力手机,近日又有新动向。格力上一次推出手机新品还是在2018年,而此次新上线的5G手机不再沿用“格力”品牌,而是换成了“大松”。格力商城页面还重点介绍了大松5G手机的“智慧互联”功能。作为格力旗下主营生活电器的品牌,大松更为市场熟悉的产品是电饭煲。 格力品牌手机悄然换成二级子品牌“大松”意味着什么?格力手机未来还有戏吗? 对此,格力电器方面12月10日在回应采访时表示,推出5G手机是集团层面的部署,格力品牌换成大松品牌是为了将格力旗下的二级子品牌更好地推向前台。 外观上,其采用6.81英寸FHD+全面屏,分辨率为1080×2400,屏幕材质为LCD,刷新率未知;同时采用极致窄边框设计,手机背部采用“S”型流光叠加全息彩虹纹理。 该机目前仅有两个版本:6GB+128GB和8GB+256GB版本,但提供了魅夜极光以及曜夜星河两种配色。 格力表示,除了传统的格力官方渠道,也会放在天猫、京东等电商平台的官方旗舰店上销售,还有董明珠的直播带货等等。 此前,董明珠表示:“格力一直坚守自主创造、自主研发、自己设计,只是需要时间,所以我从来没认为我的手机失败。” 全新的格力大松5G手机已经正式亮相,搭载高通骁龙765G处理器,采用6.81英寸FHD+分辨率的LCD单挖孔屏,前置16MP镜头,后置64MP+8MP+2MP+2MP四摄,内置5000mAh电池,支持18W充电,厚9.3mm,采用后置指纹识别方案,并且将会在拍照、电池等方面带来更多的亮点,更多详细信息,我们拭目以待。

摩登3平台登录_用大白话给你解释Zookeeper的选举机制

Zookeeper 是一个分布式服务框架,主要是用来解决分布式应用中遇到的一些数据管理问题如: 统一命名服务 、 状态同步服务 、 集群管理 、 分布式应用配置项的管理 等。 我们可以简单把 Zookeeper 理解为分布式家庭的大管家,那么管家团队是如何选出 Leader的呢?好奇吗,接下来带领大家一探究竟。 人类选举的基本原理 讲解 Zookeeper 选举过程前先来介绍一下人类的选举。 我们每个人或多或少都经历过几次选举,在投票的过程中可能会遇到这样几种情况: 情况1:自己与几个候选人都比较熟,你会将票投给你认为能力比较强的人; 熟人选举 情况2:自己也是候选人,并且与其他几个候选人都不熟,这个时候你肯定想着要去拉票,因为觉得自己才是最厉害的人呀,所有人都应该把票投给我。但是遗憾的是在拉票的过程中,你发现别人比你强,你开始自卑了,最终还是把票投给了自己认为最强的人。 自己参与选举 所有人都投完票之后,最后从投票箱中进行统计,获得票数最多的人当选。 思维导图 在整个投票过程中我们可以提炼出四个最核心的概念: 候选人能力:投票的基本原则是选最强的人。 遇强改投:如果后面发现更强的人可以改投票。 投票箱:所有人的票都会放在投票箱。 领导者:得票最多的人即为领导者。 从人类选举的原理我们来简单推导一下Zookeeper的选举原理。 Zookeeper选举的基本原理 注意如果 Zookeeper 是单机部署是不需要选举的,集群模式下才需要选举。 Zookeeper 的选举原理和人类选举的逻辑类似,套用一下人类选举的四个基本概念详细解释一下Zookeeper。 个人能力 如何衡量 Zookeeper 节点个人能力?答案是靠数据是否够新,如果节点的数据越新就代表这个节点的个人能力越强,是不是感觉很奇怪,就是这么定的! 在 Zookeeper 中通常是以事务id(后面简称zxid)来标识数据的新旧程度(版本),节点最新的zxid越大代表这个节点的数据越新,也就代表这个节点能力越强。 zxid 的全称是 ZooKeeper Transaction Id,即 Zookeeper 事务id。 遇强改投 在集群选举开始时,节点首先认为自己是最强的(即数据是最新的),然后在选票上写上自己的名字(包括zxid和sid),zxid 是事务id,sid 唯一标识自己。 紧接着会将选票传递给其他节点,同时自己也会接收其他节点传过来的选票。每个节点接收到选票后会做比较,这个人是不是比我强(zxid比我大),如果比较强,那我就需要改票,明明别人比我强,我也不能厚着脸皮对吧。 投票箱 与人类选举投票箱稍微有点不一样,Zookeeper 集群会在每个节点的内存中维护一个投票箱。节点会将自己的选票以及其他节点的选票都放在这个投票箱中。由于选票是互相传阅的,所以最终每个节点投票箱中的选票会是一样的。 领导者 在投票的过程中会去统计是否有超过一半的选票和自己选择的是同一个节点,即都认为某个节点是最强的。一旦集群中有超过半数的节点都认为某个节点最强,那该节点就是领导者了,投票也宣告结束。 什么场景下 Zookeeper 需要选举? 当 Zookeeper 集群中的一台服务器出现以下两种情况之一时,需要进入 Leader 选举。 (1)服务器初始化启动。 (2)服务器运行期间 Leader 故障。 启动时期的 Leader 选举 假设一个 Zookeeper 集群中有5台服务器,id从1到5编号,并且它们都是最新启动的,没有历史数据。 集群刚启动选举过程 假设服务器依次启动,我们来分析一下选举过程: (1)服务器1启动 发起一次选举,服务器1投自己一票,此时服务器1票数一票,不够半数以上(3票),选举无法完成。 投票结果:服务器1为1票。 服务器1状态保持为LOOKING。 (2)服务器2启动 发起一次选举,服务器1和2分别投自己一票,此时服务器1发现服务器2的id比自己大,更改选票投给服务器2。 投票结果:服务器1为0票,服务器2为2票。 服务器1,2状态保持LOOKING (3)服务器3启动 发起一次选举,服务器1、2、3先投自己一票,然后因为服务器3的id最大,两者更改选票投给为服务器3; 投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数(3票),服务器3当选Leader。 服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING。 (4)服务器4启动 发起一次选举,此时服务器1,2,3已经不是LOOKING 状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3。 服务器4并更改状态为FOLLOWING。 (5)服务器5启动 与服务器4一样投票给3,此时服务器3一共5票,服务器5为0票。 服务器5并更改状态为FOLLOWING。 最终的结果: 服务器3是 Leader,状态为 LEADING;其余服务器是 Follower,状态为 FOLLOWING。 运行时期的Leader选举 在 Zookeeper运行期间 Leader 和 非 Leader 各司其职,当有非 Leader 服务器宕机或加入不会影响 Leader,但是一旦 Leader 服务器挂了,那么整个 Zookeeper…

摩登3登录_打工人,你需要一辆电动车

作为一个合格的打工人,每天早出晚归是必不可少的,上下班的通勤问题也时刻困扰着我们。有时明明距公司只有几公里的距离,却不得不花上半小时来等公交或开车,开车又经常遇上闹心的堵车;坐地铁则被挤得喘不过气来。总之,打工人生活不易。一个操作简单又便携的交通工具,如电动车成了很好的近距离通勤解决方案。 芯讯通SIM7070G 电动车Tracker解决方案 电动车方便了我们的生活,但也容易丢失。搭载芯讯通NB模组SIM7070G的智能tracker很好的解决了这一难题。该智能tracker是在目前两车(电瓶车和摩托车)防盗技术差和交通管制困难的基础上开发的,可对两车进行全空间、全时段监控,有效提升了盗窃两车案件侦破率,从而降低盗窃两车犯罪率,实现对两车的高效管控。 实时定位 SIM7070G是一款多频段的NB-IoT 无线通信模块,采用了68PIN LCC封装。内置SIM7070G的电动车tracker,不仅功耗低,且可以实现网络全覆盖。无需复杂组网、网络接入方便,同时高精度的定位功能实时定位车辆位置。通过SIM7070G,数据请求信息可以连接至后台服务器,服务器按照产品内置密钥解密通讯报文,并解析识别产品ID和IMEI,将其进行绑定。服务器通讯通过设备的ID识别终端设备,并将消息推送到终端用户app上。因此用户可以在手机上查看电瓶车的实时位置。 防盗报警 当电动车被异常挪动时,手机上也会收到报警提示。即使电瓶车不幸丢失,也可以通过定位快速找回,减少损失。 电池监测 可以对电动车电瓶电量进行监测,确保电量始终在线,发现异常可及时发出警告。 轨迹回放 SIM7070G还支持骑行数据记录和轨迹回放。对于外卖骑手等需要精准路线的职业,可以很好的记录骑手的位置。并通过后台数据分析规划最优路线。 打工人,你骑上电动车了吗? 世健提供免费样品、参考设计以及技术指导,有成功案例。 原文转自芯讯通SIMComWirelessSolutions 关于世健   免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3平台登录_单片机数字滤波算法如何实现?(附代码)

关注、星标公众号 , 直达精彩内容 ID:技术让梦想更伟大 整理:李肖遥 单片机主要作用是控制外围的器件,并实现一定的通信和数据处理。 但在某些特定场合,不可避免地要用到数学运算,尽管单片机并不擅长实现算法和进行复杂的运算。 下面主要是介绍如何用单片机实现数字滤波。 在单片机进行数据采集时,会遇到数据的随机误差,随机误差是由随机干扰引起的,其特点是在相同条件下测量同一量时,其大小和符号会现无规则的变化而无法预测,但多次测量的结果符合统计规律。 为克服随机干扰引起的误差,硬件上可采用滤波技术,软件上可采用软件算法实现数字滤波。滤波算法往往是系统测控算法的一个重要组成部分,实时性很强。 采用数字滤波算法克服随机干扰的误差具有以下优点: 数字滤波无需其他的硬件成本,只用一个计算过程,可靠性高,不存在阻抗匹配问题。尤其是数字滤波可以对频率很低的信号进行滤波,这是模拟滤波器做不到的。 数字滤波使用软件算法实现,多输入通道可共用一个滤波程序,降低系统开支。 只要适当改变滤波器的滤波程序或运算,就能方便地改变其滤波特性,这对于滤除低频干扰和随机信号会有较大的效果。 在单片机系统中常用的滤波算法有限幅滤波法、中值滤波法、算术平均滤波法、加权平均滤波法、滑动平均滤波等。 限幅滤波算法 该运算的过程中将两次相邻的采样相减,求出其增量,然后将增量的绝对值,与两次采样允许的最大差值A进行比较。 A的大小由被测对象的具体情况而定,如果小于或等于允许的最大差值,则本次采样有效;否则取上次采样值作为本次数据的样本。 算法的程序代码如下: 1#define A  //允许的最大差值 2 3char data;  //上一次的数据 4 5char filter() 6 7{ 8 9     char  datanew;  //新数据变量 10 11    datanew=get_data();  //获得新数据变量 12 13     if((datanew- data)>A||( data-datanew>A)) 14 15         return  data; 16 17     else 18 19         return  datanew; 20 21} 说明: 限幅滤波法主要用于处理变化较为缓慢的数据,如温度、物体的位置等。使用时,关键要选取合适的门限制A。通常这可由经验数据获得,必要时可通过实验得到。 中值滤波算法 该运算的过程是对某一参数连续采样N次(N一般为奇数),然后把N次采样的值按从小到大排列,再取中间值作为本次采样值,整个过程实际上是一个序列排序的过程。 算法的程序代码如下: 1 #define N 11 //定义获得的数据个数 2 3 char filter() 4 5 { 6 7     char value_buff[N];  //定义存储数据的数组 8 9     char  count,i,j,temp; 1011     for (count= 0 ;count 1213     { 1415         value_buf[count]=get_data(); 1617         delay();  //如果采集数据比较慢,那么就需要延时或中断 1819     } 2021     for (j= 0 ;j 2223     { 2425         if (value_buff[i]>value_buff[i+ 1 ]) 2627         { 2829             temp=value_buff[i]; 3031             value_buff[i]=value_buff[i+ 1 ]; 3233             value_buff[i+ 1 ]=temp; 3435         } 3637    …

摩登3注册网址_图解FreeRTOS原理系列之任务管理器基本框架

[导读] 学习梳理一下FreeRTOS任务管理单元实现思路,代码分析基于V10.4.3。从本文开始计划写个图解freeRTOS内核系列笔记分享给朋友们,希望大家喜欢。文章中或有错误,也请留言交流指正,或加本人微信进行交流~ 本文主要学习梳理FreeRTOS任务管理器的基本原理,大体框架。 内核任务管理器需求 先来对比一下裸奔系统与RTOS应用系统的编程模型,看看两种编程的不同画风。 裸奔系统 在不用RTOS的单片机应用开发时,编程模型大概是这样的画风: 程序的主体是一个死循环,该应用程序由一系列协同工作的函数片段组成,相互实现逻辑配合,实现用户业务需求。该应用程序独占单片机,常规的单片机系统都仅有有一个计算单元核。 普通外设I/O,这里所说I/O是指广义的I/O,比如GPIO、PWM、ADC、DAC、LCD显示(当然这里并不严谨,比如ADC,DAC、LCD等也可以产生中断)等。中断函数将异步事件接收成或报文或标志或数值,在与主循环发生逻辑关联。 中断外设,比如UART、USB、I2C、定时器、DMA等根据应用需求而使用的中断。这些中断都需要相应的中断函数进行处理异步中断事件。对于输出可能采样主动输出,一般由主循环某一个动作执行;对于输入设备或许采用轮询方式,在与主循环进行耦合。 RTOS应用系统 在一个基于RTOS应用系统中,其编程模型大致是下面这样一个画风,有多个并行的任务在相对长的宏观时间维度看起来,多个任务是并行运行的,但对于常规单片机而言(一般都是单核),任一时刻只有一个任务或中断函数在独占CPU核。 常见的RTOS没有设备驱动模型,没有对外设设备进行抽象,中断函数将会由用户或调用RTOS 机制,比如event/signal等与任务进行通信 任务间还有可能需要通信,或传递消息,或完成某项需求相互间需要同步等 同样任务需要与硬件普通IO外设进行打交道,或入或出。但有可能是这个任务实现,也有可能是哪个任务执行。完全取决于开发人员如何设计。 RTOS实现任务的切入切出,切入使某任务运行;切出使某任务挂起,出让CPU,暂停运行。 RTOS充当底层支持功能,RTOS还提供丰富的时间管理,队列、邮箱等机制供应用开发使用。 …… 对于单片机而言,一般只有一个核,所有RTOS为了方便理解,可以看成是最最主要的目就是通过软件方法将硬件CPU核程序运行环境抽象为每一个应用任务虚拟出一个软核。这样从时间维度上看起来多任务是并行的,而事实上这种并行是伪并行。 上图仅仅为理解RTOS作用方便,这种虚拟核本质上并不存在,只是将硬件CPU核的运行时上下文(PC指针、状态寄存器等寄存器组、任务运行时临时变量等)通过快照保存切入切出而实现多任务的伪并行运行。 FreeRTOS任务管理器需求 从前文看出,任务管理要实现任务的切入、切出,则首先需要对任务进行抽象描述,以实现在CPU上能够实现切换。根据阅读代码以及文献加上自己的理解,将内核任务管理器的主要功能需求大致梳理成下面这样一张用例图Use case Diagram,仅仅为理解方便,或许并不严谨。 从上图,大致可以看出FreeRTOS任务调度器需要以下一些功能需求: 任务抽象描述,一个任务一般本质上是一个死循环程序片段(当然也有任务运行着会退出被杀掉的可能)。对于任务的抽象: 一般会有任务的执行主体,利用函数主体函数指针进行抽象 RTOS常规都是的基于优先级抢占调度算法,因此需要抽象出哪个任务具有更高概率能被执行,用优先级进行描述 任务需要得以切换,就需要将任务在切换间的临时状态进行保存,栈机制就能很好的满足这样的需求,因此每个任务都有一个或大或小的任务栈。其本质上是一片连续的FILO(先入后出)内存。 ….. 任务创建、删除等API接口,供应用开发使用。 任务调度器控制接口,启动调度器、停止调度器、挂起所有任务、恢复运行等调度器接口。 任务杂项信息接口,比如获取任务状态、tick信息、调试、获取任务名等API接口 任务调度算法,基于调度策略对运行时的任务进行调度,或挂起、或运行、或就绪等,主要根据调度策略管理任务的切入切出。这里主要涉及到任务间上下文切换、任务与中断函数间的上下文切换两种场景。 抽象C运行时环境,现代RTOS应用系统一般基于C语言,抽象C运行时环境,这里主要指栈,当然很多RTOS内核也内核堆,freeRTOS也不例外。熟悉C编程的朋友都知道,堆内存由malloc/free函数操作集提供用户接口,既然C堆已有,为何RTOS内核重新造轮子?为啥内核额外需要实现自己的堆管理器呢?这大体是基于下面些缘由: 编译器C堆实现,在小型嵌入式系统上有时候并不能直接使用。 C堆的实现可能相对较大,占用了较大代码空间。比较浪费有限的代码存储空间。 C堆很少是线程安全的。 C堆申请执行时间不是确定的, 执行功能所需的时间因调用而异。 C堆会在单片机有限的内存资源引发内存碎片问题。 C堆会使链接器配置复杂化。 C堆如引发未知错误,不便于调试。 FreeRTOS任务描述抽象 对于其中几项必须的关键数据域描述一下其抽象作用: pxTopOfStack:指向任务栈栈顶指针 xStateListItem:任务状态链表描述节点,用于动态将该任务添加、删除到就绪或阻塞任务对列链表中 xEventListItem:事件链表描述节点,描述本任务相关事件,用于将本任务添加到事件链表中。 uxPriority:任务优先级,用于描述本任务的优先级。 pxStack:任务栈指针,指向本任务的任务栈。 pcTaskName:任务名字符串存储区,长度可配。默认为16字节 其他的数据域,可裁剪实现一些更丰富的功能,比如主要用于防治优先级反转的优先级继承机制,trace追踪功能等。限于篇幅,也主要梳理任务管理器的主要原理,就不展开了。 任务创建删除管理 FreeRTOS为用户提供一组函数集用于任务的创建、删除等管理,先看任务的创建API: BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,      const char * const pcName,           const configSTACK_DEPTH_TYPE usStackDepth,      void * const pvParameters,      UBaseType_t uxPriority,      TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION;TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode,        const char * const pcName,            const uint32_t ulStackDepth,        void * const pvParameters,        UBaseType_t uxPriority,        StackType_t * const puxStackBuffer,        StaticTask_t * const pxTaskBuffer ) PRIVILEGED_FUNCTION;BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition,          TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION;BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition,          TaskHandle_t * pxCreatedTask ) PRIVILEGED_FUNCTION; xTaskCreate/xTaskCreateStatic 都是用于创建任务而用,其区别在于: xTaskCreate 申请任务控制块以及栈从内核堆申请 xTaskCreateStatic 创建的任务,其任务控制块内存以及任务栈内存由用户传入。或许有朋友会问StaticTask_t这不是任务控制块嘛,仔细看看其结构定义其内存对齐及大小刚好是前面说的任务控制块的定义。 xTaskCreateRestricted() /xTaskCreateRestrictedStatic(),主要用于在有或使能MPU单元的芯片中创建任务。这里的MPU是指Memory Protection Unit (MPU),不是微处理器的意思。这两者的区别与上面两个API类似,主要在于其内存分配方式不同,xTaskCreateRestricted是从内核堆动态申请,xTaskCreateRestrictedStatic用户传入。 PRIVILEGED_FUNCTION 这个宏是用于存储保护单元芯片的。 这几个任务创建函数都是用于任务创建,任务一旦创建就会被插入任务就绪链表中,当调度器调度启动后就按任务状态机根据调度策略以及外部输入事件进行调度接管。这里以xTaskCreate绘制一下其内在干了些啥: 再看看另外两个函数: void vTaskAllocateMPURegions( TaskHandle_t xTask,        const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION;void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; vTaskAllocateMPURegions: 定义一组内存保护单元(MPU)区域,供MPU受限任务使用. vTaskDelete: 删除用使用xTaskCreate()或xTaskCreateStatic()创建的任务。 任务控制管理接口 void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION;BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,                            const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION;BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION;UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION;eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;void vTaskGetInfo( TaskHandle_t xTask,                   TaskStatus_t * pxTaskStatus,                   BaseType_t xGetFreeStackSpace,                   eTaskState eState ) PRIVILEGED_FUNCTION;void vTaskPrioritySet( TaskHandle_t xTask,                       UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION;void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION;void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION;BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; 这一系列的API接口操作集主要用于对任务进行挂起延时、获取优先级、自中断函数获取优先级、挂起、恢复运行等操作。基本从其函数名就可以看出其作用。比如: vTaskDelay调用,会使调用该函数的任务进入阻塞状态一段时间,时间为传入的tick数。 这里需要注意的是有的函数在中断函数体里面不可以调用,需要使用专用版本,具体可以看看手册或注释。 调度器控制接口 void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION;void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION;void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION;BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; 这一组函数API集主要用于调度器的启动、停止控制: vTaskStartScheduler,主要用于待用户任务创建好后,硬件初始化后,启动内核调度器 vTaskEndScheduler,可用于停止内核调度器,一般很少用到,在一些安全相关的应用可能会在出故障时主动停止调度器。 vTaskSuspendAll,挂起所有任务,可以用用户逻辑主动挂起所有的任务 xTaskResumeAll,恢复所有任务为就绪态。 任务杂项API集 我根据代码及注释及自己理解,将这些API归类到杂项API集合: TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION;UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION;char * pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION;     TaskHandle_t xTaskGetHandle( const char * pcNameToQuery ) PRIVILEGED_FUNCTION;   UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;void vTaskSetApplicationTaskTag( TaskHandle_t xTask,         TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION;TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet,          BaseType_t xIndex,          void * pvValue ) PRIVILEGED_FUNCTION;void * pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery,void vApplicationStackOverflowHook( TaskHandle_t xTask,           char * pcTaskName );void vApplicationTickHook( void ); ......BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask,                                         UBaseType_t uxIndexToClear ) PRIVILEGED_FUNCTION;uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask,                                        UBaseType_t uxIndexToClear,                                        uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION;void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION;BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut,                                 TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION;BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) PRIVILEGED_FUNCTION; 这些函数具体作用就不赘述,这里仅仅梳理分类,用到时候查手册即可。 跨平台移植接口 BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION;void vTaskPlaceOnEventList( List_t * const pxEventList,                            const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;void vTaskPlaceOnUnorderedEventList( List_t * pxEventList,                                     const TickType_t xItemValue,                                     const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;void vTaskPlaceOnEventListRestricted( List_t * const pxEventList,                                      TickType_t xTicksToWait,                                      const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION;BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION;void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem,                                        const TickType_t xItemValue ) PRIVILEGED_FUNCTION;portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION;TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION;TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION;void vTaskMissedYield( void ) PRIVILEGED_FUNCTION;BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION;BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION;BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION;void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder,UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION;void vTaskSetTaskNumber( TaskHandle_t xTask,                         const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION;void vTaskStepTick( const TickType_t xTicksToJump ) PRIVILEGED_FUNCTION;eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION;TaskHandle_t pvTaskIncrementMutexHeldCount( void ) PRIVILEGED_FUNCTION;void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; 这些接口不同硬件平台需要做具化的移植,做差异化的处理,但是对于FreeRTOS统一了内部调用的接口。这样的思路在应用开发时也可以考虑使用,对于公共部分可以抽象出统一的接口,这样在不同平台上可以很方便的进行移植。对于这些接口后面有机会学习整理分享。 对于用例图中的其他部分,核心调度部分以及上下文切换,篇幅所限留在后面学习整理分享。 总结一下 本文基本学习梳理了一下对于FreeRTOS任务调度器外部接口、以及大体作用,基本组成情况,水平所限,文章中错误难免,欢迎交流指正。 END 来源:嵌入式客栈,作者:逸珺 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3注册平台官网_一个程序员的C++学习之路

来源:https://blog.csdn.net/ajian005/article/details/8003655 C++学习之路 1、why要学习C/C++/object-c? 最近想写点有用的东西,发现自己最拿手的java用起来真是笨手笨脚的。 碰到好多实现起来巨繁琐的问题,比如操作底层库,和OS framework交互,做个用户体验好点的本地图形程序等等,这些都是java的软肋。 其实,我一直对底层颇感兴趣,工作中也只能接触一下JVM,但VM毕竟是VM,始终与底层失之交臂。 Linux、Mysql、Memcached、Boost、Apache、JVM、ACE… … 很多基础服务都是用C/C++实现的,要像了解他们原理、能看懂代码、可以修改bug、增加新功能模块 开发嵌入式、中小型项目C/C++项目,维护C/C++项目… … 加深对系统底层的理解 2、How学习C/C++? 下文是我整理的一些C++图书列表,按顺序阅读,可涵盖入门到精通。 《The C programming language》必读 《C++ Primer》,号称是一本可以让你从C或JAVA程序员转为一个真正的C++程序员的入门参考书,必读。 《The C++ programming language》,C++之父,人称B教主著作,在看过C++ primer后,应该可以跳章选读。 《Think in c++》,网上说此书的中文版翻译质量奇差,推荐看影印版,选读。 《Effective c++》,类似 Effective java,讲的是最佳实践,程序员必读。 《More effective c++》,上书的补充。 《The C++ standard library》,会写C,不会用标准库怎么行。这就跟java程序员不会用java.util包一样,必读。 《Effective STL》,STL库的最佳实践。Effective C++作者又一力作,必读。 《The annotated STL source》,STL源码分析,这本书应该算是深入/精通类了,选读。 《Generic programming and STL》,号称C++编程里,就是跟模板,泛型打交道,那么精通泛型是势在必行。 《C++ Template》,C++模板编程,代码复用的经验之道,必读。 《Exceptional C++》,跟Effective C++类似,属于最佳实践和难题解析,书中列出了许多应用场景和实例代码供读者揣摩,选读。 《More Exceptional C++》,上书的补充。 《Exceptional C++ Style》,上上书的补充 《Inside The C++ Object Model》,有了上面这些书做铺垫,那么终于可以读此神书了。它会带你游览C++对象模型的底层实现机制。读完此书,任何C++代码看起来如同行云流水,必读。 3、优秀的C/C++开源项目(阅读代码) OS:Linux kernel  LVS、Linux应用程序 DB:Mysql、PostgreSQL Complier:VM、GCC Framework:OpenSip、SipProxy、 Net:ACE(Java Mina、Netty)、TCP/IP、HTTP协议栈 Cache:Memcached、Redis、 Library:STL(java util package)、Boost、Qt(UI)、 balance:Apache、Nginx … … 自学者可以参考的学习顺序(Linux C++现场全科班培训的顺序) http://www.xuanyuan-soft.cn/   Linux C++全科班课程,专注Linux/UNIX服务器端软件开发(后台开发),培养企业所需的专业Linux/UNIX C++软件工程师。课程涉及UNIX/Linux服务器端软件开发的各个方面:Linux/UNIX、C++、Boost、ACE、Oracle /MySQL、企业级的实战项目等。 上课方式 全日制脱产学习、历时4个月(120天 * 10小时 = 1200小时)。 课程模块 Linux C++全科班课程由以下模块组成: Module01 – Linux系统基础 由于本系列课程基于Linux(或UNIX),熟悉Linux操作系统是必要的前提。该模块的课程包含以下方面的内容: 常用Unix/Linux命令 熟悉文件管理、文本处理、进程管理、网络、系统管理等各个方面大约100个常用的命令。 深入了解bash了解Linux默认shell: bash 的语法、命令执行、I/O重定向、任务控制等。 正则表达式基础由于UNIX/Linux中很多强大的文本处理命令如:grep、awk、sed,还有vi编辑器等工具配合正则表达式将产生强大的威力,所以熟悉正则表达式语法是十分必要的。 find、grep、sed、awk四个强大的UNIX工具,特别是sed、awk在文本处理方面的能力非常强大,在Linux下工作应该掌握这几个命令。 Linux环境高级编程 一、课程目标 本次课程涵盖Linux系统编程的几个主要方面,通过本次课程的学习,学员将具备以下能力: 了解Linux系统调用和类库的区别; 熟悉文件I/O、文件和目录、进程管理、进程间通信(IPC)、信号、时间与定时器的编程。 二、参训要求 参加本次课程的学员须具备以下能力: 本次课程使用 C语言…

摩登3咨询:_充电桩的中场战事

配图来自Canva可画 冬天对于打工人来说总是不友好,因为他们不仅要做足心理准备爬出温暖的被窝,面对屋外凛凛寒风时,通勤也成了另一种折磨。而这时他们不免羡慕起那些可以吹着暖风开车去公司的人,但是殊不知也有一部分车主就算坐在车里也同样要面对冬天的折磨。 这是一部分开着电动车的车主,在车内穿着羽绒服哆哆嗦嗦已经是他们的常态。让他们在寒冷冬天也不敢吹暖风的根本原因只有一个——电池。续航本就引得电动车主焦虑,而开了暖风,续航焦虑就被再一次放大,使得车主不得不关上暖风裹上羽绒服,继续哆哆嗦嗦。 这个时候,缓解电动车续航焦虑的充电桩就成了这些车主的“救命稻草”,也让充电桩的市场一片火热。 多方催生的巨大市场 让充电桩市场火热的原因主要有以下几个方面。 首先是充电桩已经成为新时代的刚需。加油站对于燃油车来说是必需品,而充电桩对于电动汽车来说同样也是刚需,随着电动汽车越来越普及,电动车主对于充电桩的需求也将持续释放。 但在超级快充和超大容量电池的技术还未广泛应用之前,电动车的续航能力将会一直是整个行业的老大难问题,而充电桩作为现阶段缓解车主续航焦虑的主要手段,不仅是消费者的选择对象,也同样成为众多企业关注的重点。 其次是国家政策不断推进所释放出来的红利。在国内,充电桩的建设已经有了十年光景,但是直到近几年投入力度才逐渐加大。另外,加上今年国家将充电桩纳入新基建的范围之内,更是让这个行业有了稳固发展的基石。 根据不久前公布的《新能源汽车产业发展规划(2021-2035)》可知,到了2025年新能源汽车新车销量占比将要达到25%左右,到了2035年国内公共领域用车全面实现电动化。而按照国家提出车桩比1:1的目标来说,充电桩市场还有十分巨大的发展空间。 于是国内关于充电桩的企业也如雨后春笋一般涌出,截至2020年12月,国内与“充电桩”相关的企业就多达8.9万家。不仅有国家队的国网电动汽车,还有特斯拉、蔚来、理想等造车势力,也有特来电、南网、星星充电、小桔充电等充电桩运营商。 而市场的火热和纷涌而至的玩家,也把这场关于充电桩的战事推向了一个新的阶段。 C端成为新重点 而新阶段最直接的表现,就是赛道中的玩家都开始重点发力C端。 比如说特来电北京地区就将今年的拓展重心放在了C端市场上,试图挖掘更多有丰富资源的个人用户;而星星充电早在2016年就推出了“人人电桩”,通过共享模式切入C端;另外国网也在今年3月发起“寻找合伙人”活动,寻求与个人电桩、桩群用户等群体的合作。 通过这些动作不难看出,C端已经逐渐成为整个行业的发展重点,这种情况出现的原因主要是因为以下几点。 其一,C端的关系网会更加稳固和庞大。就像拼多多的崛起,和社交有着的很大关系一样,电动车主个人的社交网络对于充电桩企业同样重要。一个车主的推荐可以带来不少朋友,并且在经过裂变之后,也将会形成巨大的用户群体。 而且因为有自身的使用经验,在向其他车主朋友“种草”的时候,也会更有说服力,更容易吸引新用户进入整个体系之中。 其二,平台可以更了解用户需求。平台想要提供更好的服务和产品,用户的需求是必须要重视的,但是从用户端到平台之间的环节越多,所传递的信息准确度就会越低。而平台直接将流量入口放在C端,也可以保证所收集到的用户需求准确度。 作为未来大出行领域的重要流量入口,充电桩自身的互联网属性很强,而流量至上则是互联网的重点。充电桩平台通过直接对接C端,将帮助自身构建起足够大的流量池,这也就保证了企业在增量市场上获胜的前提。 流量思维背后的生态构想 流量至上的互联网思维影响下,充电桩也开始了流量的争夺战,但是在流量思维的背后,其实是充电桩企业关于自身出行生态构建的准备。 充电桩并不像看起来那样是一个孤零零的个体,其背后蕴藏着一个巨大的网络,网络中的每一个节点都是未来大出行生态的关键点。而这个生态正是充电桩企业所需要的,因为平台生态的逐渐完善将会给企业带来众多的好处。 首先,生态可以帮助稳固存量。通过社交在C端吸引的用户,保证了平台的增量市场,而将这些新涌入的用户留存在平台内,就需要更好的服务和产品来让消费者满意。 不论是充电速度还是充电是否方便,甚至是费用和服务,都是消费者急需解决的问题。这仅仅是充电层面需求,还有诸如电池保养维修、交通状况查询等多个方面的需求亟需解决,消费者当然希望自己的需求可以得到一站式解决,而生态的作用就在此凸显。 其次,平台生态化管理可以更好释放管理效能。一个较为完善的生态,可以帮助充电桩企业进一步了解不同地区的不同需求,更加精细化的了解需求并提供特定的解决方案。 具体来说,不同城市的交通状况并不相同,甚至同一个城市的不同地区交通状况也有差异,而交通状况对电动车续航能力有着很大的影响,自然也会影响到车主对充电桩的需求。通过平台生态精细化地运营,可以精确了解到这些情况,帮助企业在投放充电桩时可以避免资源的浪费。 充电桩为何“两极分化” 未来的电动车数量将会更多,日常出行也将会愈发智能,而充电桩作为未来智能出行生态的一个重要流量切入口,当下的扩张建设自然十分重要。但是在众多玩家的扩张之中,整个行业的问题也接踵而至。 最主要的问题就是现阶段充电桩十分严重的两极分化情况。具体来说,在有些城市之中,电动车主“一桩难求”的情况难以解决,而在有些地区,被投放的充电桩已经沦为“充数桩”,桩位附近杂草丛生,充电桩本身也已经完全不能使用。 这种状况的出现,表面上来看是因为充电桩企业没有针对特殊情况做出相应的对策,导致这种资源分配上的不合理,但是从更深层次来看,问题的本质却是因为因为赛道火热而盲目扩张的充电桩企业所导致的行业乱象。 因为风口的出现,让很多企业看到了机会,纷至沓来。根据天眼查专业版数据显示,我国超8成的充电桩相关企业成立于5年之内,而仅仅2019年一年,新增的充电桩相关企业数量超过2.2万家,几乎是2015年的5倍。 也就是说,行业内的玩家大多比较年轻,没有很多的技术积累。虽然相比电动汽车,充电桩的技术需求较低,但这个行业并不是不需要技术门槛,这也就要求入局者需要一定的技术实力才能保证发展道路上平稳。 电动汽车的销量正在节节攀升,未来有关于充电桩的需求也将会持续释放,但是面对有巨大增长空间的市场,参与其中的玩家并不能继续盲目扩张,更应该重视资源的合理利用,有计划地进行扩张,才能更好服务社会。

摩登3注册平台官网_并发编程中一种经典的分而治之的思想!!

写在前面 在JDK中,提供了这样一种功能:它能够将复杂的逻辑拆分成一个个简单的逻辑来并行执行,待每个并行执行的逻辑执行完成后,再将各个结果进行汇总,得出最终的结果数据。有点像Hadoop中的MapReduce。 ForkJoin是由JDK1.7之后提供的多线程并发处理框架。ForkJoin框架的基本思想是分而治之。什么是分而治之?分而治之就是将一个复杂的计算,按照设定的阈值分解成多个计算,然后将各个计算结果进行汇总。相应的,ForkJoin将复杂的计算当做一个任务,而分解的多个计算则是当做一个个子任务来并行执行。 注:文章已同步收录到:https://github.com/sunshinelyz/technology-binghe 和  https://gitee.com/binghe001/technology-binghe 。如果文件对你有点帮助,别忘记给个Star哦!如果小伙伴们有任何疑问,都可以加我微信【sun_shine_lyz】进行交流哦! Java并发编程的发展 对于Java语言来说,生来就支持多线程并发编程,在并发编程领域也是在不断发展的。Java在其发展过程中对并发编程的支持越来越完善也正好印证了这一点。 Java 1 支持thread,synchronized。 Java 5 引入了 thread pools, blocking queues, concurrent collections,locks, condition queues。 Java 7 加入了fork-join库。 Java 8 加入了 parallel streams。 并发与并行 并发和并行在本质上还是有所区别的。 并发 并发指的是在同一时刻,只有一个线程能够获取到CPU执行任务,而多个线程被快速的轮换执行,这就使得在宏观上具有多个线程同时执行的效果,并发不是真正的同时执行,并发可以使用下图表示。 并行 并行指的是无论何时,多个线程都是在多个CPU核心上同时执行的,是真正的同时执行。 分治法 基本思想 把一个规模大的问题划分为规模较小的子问题,然后分而治之,最后合并子问题的解得到原问题的解。 步骤 ①分割原问题; ②求解子问题; ③合并子问题的解为原问题的解。 我们可以使用如下伪代码来表示这个步骤。 if(任务很小){    直接计算得到结果}else{    分拆成N个子任务    调用子任务的fork()进行计算    调用子任务的join()合并计算结果} 在分治法中,子问题一般是相互独立的,因此,经常通过递归调用算法来求解子问题。 典型应用 二分搜索 大整数乘法 Strassen矩阵乘法 棋盘覆盖 合并排序 快速排序 线性时间选择 汉诺塔 ForkJoin并行处理框架 ForkJoin框架概述 Java 1.7 引入了一种新的并发框架—— Fork/Join Framework,主要用于实现“分而治之”的算法,特别是分治之后递归调用的函数。 ForkJoin框架的本质是一个用于并行执行任务的框架, 能够把一个大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务的计算结果。在Java中,ForkJoin框架与ThreadPool共存,并不是要替换ThreadPool 其实,在Java 8中引入的并行流计算,内部就是采用的ForkJoinPool来实现的。例如,下面使用并行流实现打印数组元组的程序。 public class SumArray {    public static void main(String[] args){        List  numberList = Arrays.asList( 1, 2, 3, 4, 5, 6, 7, 8, 9);         numberList.parallelStream().forEach(System.out::println);     } } 这段代码的背后就使用到了ForkJoinPool。 说到这里,可能有读者会问:可以使用线程池的ThreadPoolExecutor来实现啊?为什么要使用ForkJoinPool啊?ForkJoinPool是个什么鬼啊?! 接下来,我们就来回答这个问题。 ForkJoin框架原理 ForkJoin框架是从jdk1.7中引入的新特性,它同ThreadPoolExecutor一样,也实现了Executor和ExecutorService接口。它使用了一个无限队列来保存需要执行的任务,而线程的数量则是通过构造函数传入,如果没有向构造函数中传入指定的线程数量,那么当前计算机可用的CPU数量会被设置为线程数量作为默认值。 ForkJoinPool主要使用 分治法(Divide-and-Conquer Algorithm) 来解决问题。典型的应用比如快速排序算法。这里的要点在于,ForkJoinPool能够使用相对较少的线程来处理大量的任务。比如要对1000万个数据进行排序,那么会将这个任务分割成两个500万的排序任务和一个针对这两组500万数据的合并任务。以此类推,对于500万的数据也会做出同样的分割处理,到最后会设置一个阈值来规定当数据规模到多少时,停止这样的分割处理。比如,当元素的数量小于10时,会停止分割,转而使用插入排序对它们进行排序。那么到最后,所有的任务加起来会有大概200万+个。问题的关键在于,对于一个任务而言,只有当它所有的子任务完成之后,它才能够被执行。 所以当使用ThreadPoolExecutor时,使用分治法会存在问题,因为ThreadPoolExecutor中的线程无法向任务队列中再添加一个任务并在等待该任务完成之后再继续执行。而使用ForkJoinPool就能够解决这个问题,它就能够让其中的线程创建新的任务,并挂起当前的任务,此时线程就能够从队列中选择子任务执行。 那么使用ThreadPoolExecutor或者ForkJoinPool,性能上会有什么差异呢? 首先,使用ForkJoinPool能够使用数量有限的线程来完成非常多的具有父子关系的任务,比如使用4个线程来完成超过200万个任务。但是,使用ThreadPoolExecutor时,是不可能完成的,因为ThreadPoolExecutor中的Thread无法选择优先执行子任务,需要完成200万个具有父子关系的任务时,也需要200万个线程,很显然这是不可行的,也是很不合理的!! 工作窃取算法 假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。 工作窃取算法的优点:充分利用线程进行并行计算,并减少了线程间的竞争。 工作窃取算法的缺点:在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且该算法会消耗更多的系统资源,比如创建多个线程和多个双端队列。 Fork/Join框架局限性: 对于Fork/Join框架而言,当一个任务正在等待它使用Join操作创建的子任务结束时,执行这个任务的工作线程查找其他未被执行的任务,并开始执行这些未被执行的任务,通过这种方式,线程充分利用它们的运行时间来提高应用程序的性能。为了实现这个目标,Fork/Join框架执行的任务有一些局限性。 (1)任务只能使用Fork和Join操作来进行同步机制,如果使用了其他同步机制,则在同步操作时,工作线程就不能执行其他任务了。比如,在Fork/Join框架中,使任务进行了睡眠,那么,在睡眠期间内,正在执行这个任务的工作线程将不会执行其他任务了。(2)在Fork/Join框架中,所拆分的任务不应该去执行IO操作,比如:读写数据文件。(3)任务不能抛出检查异常,必须通过必要的代码来出来这些异常。 ForkJoin框架的实现 ForkJoin框架中一些重要的类如下所示。 ForkJoinPool 框架中涉及的主要类如下所示。 1.ForkJoinPool类 实现了ForkJoin框架中的线程池,由类图可以看出,ForkJoinPool类实现了线程池的Executor接口。 我们也可以从下图中看出ForkJoinPool的类图关系。 其中,可以使用Executors.newWorkStealPool()方法创建ForkJoinPool。 ForkJoinPool中提供了如下提交任务的方法。 public void execute(ForkJoinTask  task)public void execute(Runnable task)public   T  invoke (ForkJoinTask  task) public   List >  invokeAll (Collection > tasks)  public   ForkJoinTask…