400-915-3552 武汉诺和诺诚生物科技有限公司湖北服务中心,湖北省细胞服务中心主营细胞储存、基因检测

您的位置: 亲子鉴定

亲子鉴定dna比对值(亲子鉴定比对的是什么)

0 次浏览 编辑 基因细胞服务中心
2023-02-10 17:06:29


什么是DNA亲子鉴定?

通过遗传标记的检验与分析来判断父母与子女是否亲生关系,称之为亲子关系或。DNA是人体遗传的基本载体,人类的染色体是由DNA构成的,每个人体细胞有23对(46条)成对的染色体,其分别来自父亲和母亲。夫妻之间各自提供的23条染色体,在受精后相互配对,构成了23对(46条)孩子的染色体。如此循环往复构成生命的延续。由于人体约有30亿个核苷酸构成整个染色体系统,而且在生殖细胞形成前的互换和组合是随机的,所以除同卵双胞胎以外,没有任何两个人具有完全相同的核苷酸序列,这就是人的遗传多态性。尽管遗传多态性的存在,但每一个人的染色体必然也只能来自其父母,这就是DNA亲子鉴定的理论基础。



什么是DNA亲子鉴定?

  司法鉴定是指在诉讼活动中鉴定人运用科学技术或者专门知识对诉讼涉及的专门性问题进行鉴别和判断并提供鉴定意见的活动是我国主要的法律鉴定制度。DNA亲子鉴定80%以上都没有启动司法鉴定都属于个人鉴定。个人鉴定是鉴定机构对个人的遗传基因所做的生物学鉴定在取样环节不需要公检法出具委托书也不完全需要申请人提供身份证等证件和同意书仅做为鉴定人亲权鉴定的一个凭证并可作为证据之一但鉴定流程、鉴定结果准确性是和司法鉴定一致的。



网友:亲子鉴定dna比对值

作者:顺风车

部门:数据中台

来源:微信公众号:有赞coder

出处:?__biz=MzAxOTY5MDMxNA==&mid=2455761951&idx=1&sn=2164bae245ecd953265c738ca48fd887

一、介绍

数据资产治理(详情见: 数据资产,赞之治理 )的前提要有数据。它要求数据类型全、量大,并尽可能多地覆盖数据流转的各个环节。元数据采集就变得尤其重要,它是数据资产治理的核心底座。

在早期的采集系统,我们主要面向数仓,通过“API直连方式”采集Hive/Mysql表的元数据。随着业务的快速发展,数据运营、成本治理的需求越来越强烈。元数据需要覆盖到数据全链路,包括离线计算平台、实时计算平台、内部工具,任务元数据等。采集元数据的过程中,我们遇到以下困难:

数据类别多需要采集组件的基础元数据、趋势数据、资源数据、任务数据和血缘数据。平台组件多大数据平台组件:Hive/Hbase/Kafka/Druid/Flume/Flink/Presto,内部工具:BI报表系统/指标库/OneService等。采集周期长接入新的数据类型周期长,需要经过需求评审、开发、测试、联调、数据核对、上线。接入效率低,采集稳定性接入每种数据类型需要和业务方对接,效率不高,采集过程出现异常中断,不能及时感知到。

本文主要从元数据的含义、提取、采集、监控告警几个方面,介绍我们做的一些事情。

二、元数据2.1 什么是元数据

什么是元数据?元数据是“用来描述数据的数据”。举个例子:我拿手机拍摄了一张照片,查看照片的详情,如下所示信息:

照片信息文件名:IMG_20201217_114115时间:2020年12月17号 11:30:01分辨率:4608X2592文件大小:2.69MB相机制造商:OnePlus相机型号:ONEPLUS A5000闪光灯:未使用闪光灯焦距:4.10mm白平衡:自动光圈:f/1.7曝光时间:1/50ISO:1250

这些就是数码照片的元数据,用来描述一张图片。在资产治理平台中,我们采集Hive组件的元数据包含:表名称、字段列表、责任人、任务调度信息等。

收集全链路的数据(各类元数据),可以帮助数据平台回答:我们有哪些数据?有多少人在使用?数据存储是多少?如何查找这些数据?数据的流转是怎么样的?结合血缘关系进行问题溯源和影响分析。

2.2 采集了哪些元数据

如下图所示,是一张数据的流转图,我们主要采集了各个平台组件的:

基础元数据表名称、备注、字段列表、责任人、业务域、表所在集群、项目等信息。趋势数据表的大小、行数、文件数、分区数、任务调度时长、产出时间等信息。资源数据集群的吞吐量、QPS、调度任务消耗Cpu、内存大小等信息。血缘数据表/字段级别的上下游依赖关系、任务的输入输出表依赖关系。任务数据离线/实时计算任务的名称、责任人、deadline告警时间、脚本、任务配置等信息。

截至目前为止,采集到的平台组件覆盖了整个数据的链路。覆盖数据类型10种+,基础元数据的数量10w+。主要包括:

离线平台组件Hive/Mysql。实时平台Flume/Kafka/Hbase/Kylin/Es/Presto/Spark/Flink等。内部工具BI报表系统、指标库系统、OneService、测试QA系统。

三、元数据提取

如何从众多平台组件提取元数据呢?大致有这几个方面:

访问Metastore获取基础元数据一般的平台组件会把元数据存储在Mysql等关系型数据库中,通过访问Metastore获取基础的元数据。获取组件集群资源数据平台组件本身会提供Metrics和Alarm的监控服务,定期请求服务,把数据流入到Hbase/Opentsdb等存储。通过访问存储并对指标数据进行汇总统计,获取集群或任务的资源数据。获取平台组件业务指标数据中台内部有各种平台,比如KP(Kafka基础平台)、RP(Flink实时计算平台)、RDS(详情见:管理Mysql的工具平台)、DP(详情见:数据研发平台)。通过这些平台自身提供的服务获取基础元数据、业务监控指标、集群QPS、吞吐量等数据。获取血缘数据用户在DP平台、RP平台上开发计算任务,我们可以及时的获取发布的任务列表、任务的配置信息、SQL脚本等信息。计算任务通过解析任务的输入/输出依赖配置,获取血缘关系。SQL类型任务通过“Sql Parser”(使用ANTLR4系统实现的sql改写工具)工具解析SQL脚本获取表/字段级别的血缘关系。3.1 离线平台

主要是采集Hive/RDS表的元数据。

Hive组件的元数据存储在Metastore,通过JDBC的方式访问Mysql获取库表的元数据。根据Hive表信息组装成HDFS地址,通过FileSystem API获取文件的状态、文件数、文件大小、数据更新时间等趋势数据。

RDS平台提供了对Mysql服务的管理,通过平台提供的服务接口获取表的元数据、趋势数据、访问情况等信息。

3.2 实时平台

主要是Flume/Hbase/Kafka等组件的元数据。

举个例子:我们通过访问KP平台落盘的工单数据,获取topic的基础元数据信息,定时消费topic获取抽样数据,解析出字段列表。平台本身提供集群状态和业务监控指标,通过平台服务获取集群资源的使用情况。

3.3 内部工具

主要是BI报表系统(一张BI报表查询的Hive表、Mysql表关系)、指标库(指标关联的Hive表和字段关系)、OneService服务(接口访问哪些库表的关系数据)的血缘数据。

这些内部系统在产品不断迭代中积累了很多元数据。在不考虑元数据时效性的情况下,我们一般同步这些系统的数据到Hive库,经过离线处理得到元数据。

3.4 任务元数据

元数据任务主要是DP离线任务、Flink计算服务、Flume任务。

这些计算任务都有落盘,通过Binlog同步或离线同步的方式获取任务列表,得到任务的元数据。

四、数据采集

经过元数据提取,我们可以获取数据全链路中各个平台组件的元数据。数据采集指的是把这些元数据入库到数据资产管理系统的数据库中。

4.1 采集方式

采集数据主要有3种方式,下方表格列出了3种方式的优缺点:

一般情况我们推荐业务方使用采集SDK。主动上报元数据,在接入时只需要关注上报的数据格式、SDK的初始化,就能快速完成上报工作。

4.2 采集SDK设计

采集SDK支持上报基础元数据、趋势数据、血缘数据,主要包括客户端SDK和采集服务端两部分。客户端SDK主要实现了通用上报模型的定义和上报功能,采集服务端主要实现了不同的适配器,完成数据的统一入库。

4.2.1 架构

采集SDK客户端

定义了基础元数据(MetaSchema)、趋势数据(TrendSchema)、血缘数据(LineageSchema)的通用模型,支持扩展新的上报模型(XXXSchema)。ReportService实现了数据推送到Kafka的功能。

采集服务端

数据鉴权服务端消费kafka,获取到数据后,对每条记录的签名进行认证(取到记录中的appId、appName、token信息,重新生成token并比对值的过程)。统一入库服务定义统一数据入库模型,包括表基础元数据、趋势数据、血缘数据、趋势数据并实现不同数据类型入库的服务。数据适配器Bridge获取kafka的数据,根据不同的数据类型转换成“统一入库模型”,并触发“统一入库服务”完成数据的写入。4.2.2 通用模型

采集的平台组件多,我们参照Hive“表模型”的定义,抽象出了一套通用的数据上报模型,保障了数据上报和数据存储的扩展性。

通用元数据模型主要包括接入方信息、表基础信息、业务域信息、扩展信息。通用趋势模型主要包括表信息定义、趋势指标定义、扩展信息。通用血缘模型一张血缘图主要是由点、线组成的。点指的是表节点,边指的是任务节点;节点信息包括:节点名称、节点类型、节点扩展信息;表节点包括表基础信息,可以唯一确定一张表,任务节点包括任务的基础信息。

通用血缘模型主要包含表血缘模型定义、表任务血缘模型定义,支持用户单独上报表血缘、任务血缘。模型定义如下:

/** * 表血缘模型定义 */@Datapublic class TableLineageSchema<T extends TableNode> {/*** 当前节点*/private T current;/*** 父节点*/private List<T> parents;/*** 子节点*/private List<T> childs;/*** 表级别血缘扩展信息,json对象,kv结构*/private String extParam;}

/** * 表任务血缘定义 * */@Datapublic class JobLineageSchema<Job extends JobNode, Table extends TableNode> {/*** 任务节点对象*/private Job task;/*** 输入对象列表*/private List<Table> inputs;/*** 输出对象列表*/private List<Table> outputs;/*** 任务级别血缘扩展信息,json对象,kv结构*/private String extParam;}

每种模型定义留有扩展字段(约定json格式),不在定义中的指标可以放在扩展字段,上报数据后,也会被存储在元数据表的扩展字段中。接入新的类型,指标定义差异性较大,通过扩展新的数据模型定义,完成元数据的上报。

4.2.3 接入,校验,限流

如何保障用户上报的数据是安全的?我们设计了一组签名:接入方Id(appId)、接入名称(appName)、接入标识(token)。管理员填写基础的接入方信息即可生成随机的appId、token信息。业务方在初始化采集SDK时,指定签名信息,上报的每条数据会携带签名。在采集服务端,会对每条数据进行签名认证,这样保障了数据的安全性。

采集SDK会对上报的每条数据执行通用规则,检查数据的合法性,比如表名称不为空、责任人有效性、表大小、趋势数据不能为负数等。检查出非法数据会过滤掉并触发告警通知。

在采集SDK服务端,定时(每隔两秒)消费Kafka一批数据,支持设置消费数据的时间间隔和拉取条数,不会因为上报数据的流量波峰导致下游入库压力变大,起到了限流的作用。

4.3 触发采集

我们支持了元数据的多种采集方式,如何触发数据的采集呢?整体的思路是:

增量采集变更的数据定期采集全量的数据实时采集SDK上报的数据

基于阿波罗配置系统(详见: Apollo在有赞的实践 )和Linux系统的Crontab功能,实现了任务的定时调度。数据采集任务配置在阿波罗上,变更配置后发布阿波罗,实时同步配置信息到线上节点的Crontab文件中。

4.3.1 增量任务,准实时

支持获取组件最近变更的元数据,配置增量任务,提升元数据采集的实时性。比如增量采集Hive表元数据,每隔1分钟查询metastore获取最近变更的元数据列表,更新元数据。

4.3.2 全量任务,兜底

增量采集可能存在丢数据的场景,每隔1天或多天全量采集一次,作为兜底方案保障元数据的完整性。

4.3.3 采集SDK,实时上报

采集SDK支持实时和全量上报模式,一般要求接入方数据变更后实时上报,同时不定期全量上报一次。

4.4 数据存储,更新

数据采集后,要考虑如何存储、元数据发生变更如何同步更新。我们对采集过来的元数据进行分类归一,抽象出“表模型”,分类存储。

4.4.1 数据存储

我们评估了每种组件的元数据数量(总量10w+)、预估数据可能的使用场景,最终选择Mysql存储。为了满足用户的个性化查询需求,构建Es宽表。以表粒度为主包括:表名称、备注、责任人、字段列表、趋势信息、业务域信息、任务信息等。数据采集过程中同步更新Es表保障了元数据查询的实时性,定期(构建离线模型表,每天同步更新Es表)全量更新一次,保障了元数据的完整性。

元数据中的表不是孤立存在的,一般有关联的任务(离线任务,实时任务)产出表,数据地图中也会展示表和任务的流转关系。那么在众多的平台组件中,如何唯一的区分出一张表呢?我们是通过表所在的集群名称、项目名称、表类型(来自哪个平台组件)、表名称这几个字段组合来唯一区分。

数据分类储存,最终形成:基础元数据表、趋势数据表、任务元数据表、血缘数据表。

4.4.2 数据更新

元数据表下线了,如何同步更新呢?

全量采集,找差异全量采集时,获取平台组件的所有元数据,和资产数据库中的元数据表做全量比对,找出差异的表并设置下线。增量采集,走约定增量采集时,与接入方约定:已下线的表不上报,3天未更新的元数据平台会进行清理。五、监控预警

完成了数据的采集,是不是就大功告成了?答案是否定的。采集过程中数据类型多、删掉方式多样、删掉链路长,任何一个环节出现问题就会导致结果的不准确。我们通过以下方式保障采集服务的稳定性。

5.1 采集链路监控告警5.1.1 接口监控

我们把系统所有的服务接口划分为三个等级:核心、重要、一般,并支持注解的方式打标接口和责任人,发生异常触发不同程度的告警通知。核心服务异常直接触发电话告警,重要或一般服务异常触发邮件告警。系统会存储接口请求和执行的状态删掉,每天定时向接口服务的责任人发送服务日报。通过将元数据的采集服务标记为核心和重要服务,对“API直连方式”的接口做到了异常感知。

如下所示,是服务接口的告警通知:

[Warning][prod][data-dict] - 数据资产平台告警你负责的[元信息采集]模块(backup为XXX)出现[重要]等级问题, 方法名:[com.youzan.bigdata.crystal.controller.HiveMetaController.getHiveDb], 异常信息:nullhost:XXXXXX处理地址:

如下所示,是服务接口的告警日报:

[Warning][prod][data-dict] - 数据资产平台告警[shunfengche]今日问题汇总请及时收敛今日问题,总问题数 1 个,出现 2 次【核心】问题 0 个:【重要】问题 0 个:【一般】问题 1 个:[数据采集]com.youzan.bigdata.crystal.controller.HiveMetaController.getHiveDb 今日出现 2 次, 已存在 5 天, 历史出现 8 次host:XXXXXX处理地址: 采集过程监控

针对每个元数据采集服务,采集过程发生异常则发送告警通知。

如下所示,是采集过程发生异常触发的告警:

[Warning][prod][data-dict] - 数据资产平台告警你负责的[元信息采集]模块(backup为XXX)出现[一般]等级问题, 方法名:[com.youzan.bigdata.crystal.asyncworker.work.AsyncAllRdsDDLWorker.run], 异常信息:/n### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: Query execution was interrupted5.1.3 kafka消息积压告警

消费kafka数据,通过kp平台配置消息积压告警,针对采集SDK服务做到了异常感知。

5.2 结果数据比对

主要是事后监控预警,定时探查采集的元数据数量异常波动。对不同类型的元数据,通过比较当日采集的数量和近7天历史平均的数量,设定异常波动告警阈值,超过阈值触发告警通知。

针对采集的元数据结果表,配置一些数据质量探查规则,定时执行异常规则发现问题数据触发告警通知。这样保障了对结果数据的异常感知。比如已定义的数据质量规则:

表责任人:离职人员或特殊责任人(表的责任人是app,admin等)数量。血缘:无关联的任务,无上下游的表数量。趋势数据:表趋势值非法(默认值-1)的数量。业务域:表所属的业务域值为-1(非法值)的数量。5.3 项目迭代机制,采集问题收敛

经过事前、事中、事后的监控告警机制,能够及时发现并感知采集异常。对异常问题,我们一般以项目迭代的方式,发起jira,组织关联人复盘。追溯根因,讨论改进方案,产出action,定期关注并持续做到问题的收敛。

六、总结和展望6.1 总结

我们定义了一套通用的数据采集和存储模型,支持接入不同数据类型的元数据,支持多种接入方式,采集SDK提升了接入效率和数据的时效性。

如下图所示,目前已接入了各种组件的元数据,并对数据分类统一管理,提供数据字典、数据地图、资产大盘、 全域成本账单 等元数据的应用。

如果把数据资产治理比作建设高楼大厦,那么不同组件的元数据就是原材料,数据采集就是地基。只有地基打得牢固,数据治理这座大厦才会越建越稳。

6.2 展望

数据采集的过程中我们也遇到很多的问题,在后续的工作中需要不断的优化和功能迭代,包括但不限于:

自动化采集目前接入新的数据类型,需要和接入方确认数据上报格式,编写数据适配器。后续考虑自动化采集,减少人工介入。接入工单系统,接入方发起工单申请,填报基础的元数据信息,管理员审批后,能够根据工单信息自动生成数据适配器,完成数据的上报。采集任务管理目前接入了各种组件的元数据,采集任务数25+,新增采集任务或任务下线,需要走阿波罗配置系统。采集任务管理、搜索、任务启停需求越来越强烈。提升元数据质量接入的元数据类型、元数据服务越来越多,对元数据的质量提出了更高的要求。如何保障数据的准确性、可用性,是后续重点要考虑的事情。·支持业务元数据接入目前主要接入了数据平台组件的元数据,业务方元数据占比较小,后续考虑支持快速接入业务数据,支持非结构化数据的采集和存储。

作者:顺风车

今天的文章我首先说一下之前文章里的思考题的解决思路,我会给出完整可运行的代码。之后通过观察程序的运行结果里的现象简单介绍 Go 语言的调度器是如何对 goroutine 进行调度的。

回答之前的问题

先来回顾一下上周文章里思考题的题目:

假设有一个超长的切片,切片的元素类型为int,切片中的元素为乱序排列。限时5秒,使用多个goroutine查找切片中是否存在给定值,在找到目标值或者超时后立刻结束所有goroutine的执行。

比如切片为:[23, 32, 78, 43, 76, 65, 345, 762, ...... 915, 86],查找的目标值为345,如果切片中存在目标值程序输出:"Found it!"并且立即取消仍在执行查找任务的 goroutine 。如果在超时时间未找到目标值程序输出:"Timeout! Not Found",同时立即取消仍在执行查找任务的 goroutine 。

首先题目里提到了 在找到目标值或者超时后立刻结束所有goroutine的执行 ,完成这两个功能需要借助计时器、通道和 context 才行。我能想到的第一点就是要用 context.WithCancel 创建一个上下文对象传递给每个执行任务的 goroutine ,外部在满足条件后(找到目标值或者已超时)通过调用上下文的取消函数来通知所有 goroutine 停止工作。

func main() {timer := time.NewTimer(time.Second * 5)ctx, cancel := context.WithCancel(context.Background())resultChan := make(chan bool) ......select {case <-timer.C:fmt.Fprintln(os.Stderr, "Timeout! Not Found")cancel()case <- resultChan:fmt.Fprintf(os.Stdout, "Found it! ")cancel()}}

执行任务的 goroutine 们如果找到目标值后需要通知外部等待任务执行的主 goroutine ,这个工作是典型的应用通道的场景,上面代码也已经看到了,我们创建了一个接收查找结果的通道,接下来要做的就是把它和上下文对象一起传递给执行任务的 goroutine 。

func SearchTarget(ctx context.Context, data []int, target int, resultChan chan bool) {for _, v := range data {select {case <- ctx.Done():fmt.Fprintf(os.Stdout, "Task cancelded! ")returndefault:}// 模拟一个耗时查找,这里只是比对值,真实开发中可以是其他操作fmt.Fprintf(os.Stdout, "v: %d ", v)time.Sleep(time.Millisecond * 1500)if target == v {resultChan <- truereturn}}}

在执行查找任务的 goroutine 里接收上下文的取消信号,为了不阻塞查找任务,我们使用了 select 语句加 default 的组合:

select {case <- ctx.Done():fmt.Fprintf(os.Stdout, "Task cancelded! ")returndefault:}

在 goroutine 里面如果找到了目标值,则会通过发送一个 true 值给 resultChan ,让外面等待的主 goroutine 收到一个已经找到目标值的信号。

resultChan <- true

这样通过上下文的 Done 通道和 resultChan 通道, goroutine 们就能相互通信了。

Go 语言中最常见的、也是经常被人提及的设计模式 — 不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存

完整的源代码如下:

package mainimport ("context""fmt""os""time")func main() {timer := time.NewTimer(time.Second * 5)data := []int{1, 2, 3, 10, 999, 8, 345, 7, 98, 33, 66, 77, 88, 68, 96}dataLen := len(data)size := 3target := 345ctx, cancel := context.WithCancel(context.Background())resultChan := make(chan bool)for i := 0; i < dataLen; i += size {end := i + sizeif end >= dataLen {end = dataLen - 1}go SearchTarget(ctx, data[i:end], target, resultChan)}select {case <-timer.C:fmt.Fprintln(os.Stderr, "Timeout! Not Found")cancel()case <- resultChan:fmt.Fprintf(os.Stdout, "Found it! ")cancel()}time.Sleep(time.Second * 2)}func SearchTarget(ctx context.Context, data []int, target int, resultChan chan bool) {for _, v := range data {select {case <- ctx.Done():fmt.Fprintf(os.Stdout, "Task cancelded! ")returndefault:}// 模拟一个耗时查找,这里只是比对值,真实开发中可以是其他操作fmt.Fprintf(os.Stdout, "v: %d ", v)time.Sleep(time.Millisecond * 1500)if target == v {resultChan <- truereturn}}}

为了打印演示结果所以加了几处 time.Sleep ,这个程序更多的是提供思路框架,所以细节的地方没有考虑。有几位读者把他们的答案发给了我,其中有一位的提供的答案在代码实现上考虑的更全面,这个我们放到文末再说。

上面程序的执行结果如下:

v: 1 v: 88 v: 33 v: 10 v: 345 Found it!v: 2 v: 999 Task cancelded! v: 68 Task cancelded! Task cancelded!

因为是并发程序所以每次打印的结果的顺序是不一样的,这个你们可以自己试验一下。而且也并不是先开启的 goroutine 就一定会先执行,主要还是看调度器先调度哪个。

Go语言调度器

所有应用程序都是运行在操作系统上,真正用来干活(计算)的是 CPU 。所以谈到 Go 语言调度器,我们也绕不开操作系统、进程与线程这些概念。线程是操作系统调度时的最基本单元,而 Linux 在调度器并不区分进程和线程的调度,它们在不同操作系统上也有不同的实现,但是在大多数的实现中线程都属于进程。

多个线程可以属于同一个进程并共享内存空间。因为多线程不需要创建新的虚拟内存空间,所以它们也不需要内存管理单元处理上下文的切换,线程之间的通信也正是基于共享的内存进行的,与重量级的进程相比,线程显得比较轻量。

虽然线程比较轻量,但是在调度时也有比较大的额外开销。每个线程会都占用 1 兆以上的内存空间,在对线程进行切换时不止会消耗较多的内存,恢复寄存器中的内容还需要向操作系统申请或者销毁对应的资源。

大量的线程出现了新的问题

高内存占用调度的CPU高消耗

然后工程师们就发现,其实一个线程分为"内核态"线程和"用户态"线程。

一个 用户态线程 必须要绑定一个 内核态线程 ,但是CPU并不知道有 用户态线程 的存在,它只知道它运行的是一个 内核态线程 (Linux的PCB进程控制块)。这样,我们再去细化分类,内核线程依然叫线程(thread),用户线程叫协程(co-routine)。既然一个协程可以绑定一个线程,那么也可以通过实现协程调度器把多个协程与一个或者多个线程进行绑定。

Go 语言的 goroutine 来自协程的概念,让一组可复用的函数运行在一组线程之上,即使有协程阻塞,该线程的其他协程也可以被 runtime 调度,转移到其他可运行的线程上。最关键的是,程序员看不到这些底层的细节,这就降低了编程的难度,提供了更容易的并发。

Go 中,协程被称为 goroutine ,它非常轻量,一个 goroutine 只占几KB,并且这几KB就足够 goroutine 运行完,这就能在有限的内存空间内支持大量 goroutine ,支持了更多的并发。虽然一个 goroutine 的栈只占几KB,但实际是可伸缩的,如果需要更多内存, runtime 会自动为 goroutine 分配。

既然我们知道了 goroutine 和系统线程的关系,那么最关键的一点就是实现协程调度器了。

Go 目前使用的调度器是2012年重新设计的,因为之前的调度器性能存在问题,所以使用4年就被废弃了。重新设计的调度器使用 G-M-P 模型并一直沿用至今。

调度器G-M-P模型

G — 表示 goroutine,它是一个待执行的任务;M — 表示操作系统的线程,它由操作系统的调度器调度和管理;P — 表示处理器,它可以被看做运行在线程上的本地调度器;G

gorotuine 就是 Go 语言调度器中待执行的任务,它在运行时调度器中的地位与线程在操作系统中差不多,但是它占用了更小的内存空间,也降低了上下文切换的开销。

goroutine 只存在于 Go 语言的运行时,它是 Go 语言在用户态提供的线程,作为一种粒度更细的资源调度单元,如果使用得当能够在高并发的场景下更高效地利用机器的 CPU 。

M

Go 语言并发模型中的 M 是操作系统线程。调度器最多可以创建 10000 个线程,但是其中大多数的线程都不会执行用户代码(可能陷入系统调用),最多只会有 GOMAXPROCS 个活跃线程能够正常运行。

在默认情况下,运行时会将 GOMAXPROCS 设置成当前机器的核数,我们也可以使用 runtime.GOMAXPROCS 来改变程序中最大的线程数。一个四核机器上会创建四个活跃的操作系统线程,每一个线程都对应一个运行时中的 runtime.m 结构体。

在大多数情况下,我们都会使用 Go 的默认设置,也就是活跃线程数等于 CPU 个数,在这种情况下不会触发操作系统的线程调度和上下文切换,所有的调度都会发生在用户态,由 Go 语言调度器触发,能够减少非常多的额外开销。

操作系统线程在 Go 语言中会使用私有结构体 runtime.m 来表示

type m struct {g0*gcurg *g...}

其中 g0 是持有调度栈的 goroutine , curg 是在当前线程上运行的用户 goroutine ,用户 goroutine 执行完后,线程切换回 g0 上, g0 会从线程 M 绑定的 P 上的等待队列中获取 goroutine 交给线程。

P

调度器中的处理器 P 是线程和 goroutine 的中间层,它能提供线程需要的上下文环境,也会负责调度线程上的等待队列,通过处理器 P 的调度,每一个内核线程都能够执行多个 goroutine ,它能在 goroutine 进行一些 I/O 操作时及时切换,提高线程的利用率。因为调度器在启动时就会创建 GOMAXPROCS 个处理器,所以 Go 语言程序的处理器数量一定会等于 GOMAXPROCS ,这些处理器会绑定到不同的内核线程上并利用线程的计算资源运行 goroutine 。

此外在调度器里还有一个全局等待队列,当所有P本地的等待队列被占满后,新创建的 goroutine 会进入全局等待队列。 P 的本地队列为空后, M 也会从全局队列中拿一批待执行的 goroutine 放到 P 本地的等待队列中。

GMP模型图示

GMP模型图示

全局队列:存放等待运行的G。P的本地队列:同全局队列类似,存放的也是等待运行的G,存的数量有限,不超过256个。新建G时,G优先加入到P的本地队列,如果队列已满,则会把本地队列中一半的G移动到全局队列。P列表:所有的P都在程序启动时创建,并保存在数组中,最多有GOMAXPROCS(可配置)个。M:线程想运行任务就得获取P,从P的本地队列获取G,P队列为空时,M也会尝试从全局队列拿一批G放到P的本地队列,或从其他P的本地队列偷一半放到自己P的本地队列。M运行G,G执行之后,M会从P获取下一个G,不断重复下去。goroutine 调度器和OS调度器是通过M结合起来的,每个M都代表了1个内核线程,OS调度器负责把内核线程分配到CPU上执行。调度器的策略

调度器的一个策略是尽可能的复用现有的活跃线程,通过以下两个机制提高线程的复用:

work stealing机制,当本线程无可运行的G时,尝试从其他线程绑定的P偷取G,而不是销毁线程。hand off机制,当本线程因为G进行系统调用阻塞时,线程释放绑定的P,把P转移给其他空闲的线程执行。

Go 的运行时并不具备操作系统内核级的硬件中断能力,基于工作窃取的调度器实现,本质上属于先来先服务的协作式调度,为了解决响应时间可能较高的问题,目前运行时实现了协作式调度和抢占式调度两种不同的调度策略,保证在大部分情况下,不同的 G 能够获得均匀的 CPU 时间片。

协作式调度依靠被调度方主动弃权,系统监控到一个 goroutine 运行超过10ms会通过 runtime.Gosched 调用主动让出执行机会。抢占式调度则依靠调度器强制将被调度方被动中断。


亲子鉴定dna比对值(亲子鉴定比对的是什么)

文章内容可能有引用其它网络内容,如有侵害您的权益,请联系我们免费删除:https://www.whnhnc.com/dna/

  • 细胞存储问答
  • 亲子鉴定了解
  • 细胞应用咨询
  • 基因检测咨询
  • 其它百科咨询
微信扫一扫

Copyright@2011-2018 All Rights Reserved 版权所有: © 2020 武汉诺和诺诚生物科技有限公司 鄂ICP备2022020202号-5

首页 / 亲子鉴定 / 细胞储存 /服务项目 /