精华内容
下载资源
问答
  • DPDK 全面分析

    万次阅读 2019-09-15 00:30:29
    DPDK 全面分析 高性能网络技术 随着云计算产业的异军突起,网络技术的不断创新,越来越多的网络设备基础架构逐步向基于通用处理器平台的架构方向融合,从传统的物理网络到虚拟网络,从扁平化的网络结构到基于 SDN...

    DPDK 全面分析

    高性能网络技术

    随着云计算产业的异军突起,网络技术的不断创新,越来越多的网络设备基础架构逐步向基于通用处理器平台的架构方向融合,从传统的物理网络到虚拟网络,从扁平化的网络结构到基于 SDN 分层的网络结构,无不体现出这种创新与融合。

    这在使得网络变得更加可控制和成本更低的同时,也能够支持大规模用户或应用程序的性能需求,以及海量数据的处理。究其原因,其实是高性能网络编程技术随着网络架构的演进不断突破的一种必然结果。

    C10K 到 C10M 问题的演进

    如今,关注的更多是 C10M 问题(即单机 1 千万个并发连接问题)。很多计算机领域的大佬们从硬件上和软件上都提出了多种解决方案。从硬件上,比如说,现在的类似很多 40Gpbs、32-cores、256G RAM 这样配置的 X86 服务器完全可以处理 1 千万个以上的并发连接。

    但是从硬件上解决问题就没多大意思了,首先它成本高,其次不通用,最后也没什么挑战,无非就是堆砌硬件而已。所以,抛开硬件不谈,我们看看从软件上该如何解决这个世界难题呢?

    这里不得不提一个人,就是 Errata Security 公司的 CEO Robert Graham,他在 Shmoocon 2013 大会上很巧妙地解释了这个问题。有兴趣可以查看其 YouTube 的演进视频: C10M Defending The Internet At Scale

    431521-20180202125016203-1852422480.png

    他提到了 UNIX 的设计初衷其实为电话网络的控制系统而设计的,而不是一般的服务器操作系统,所以,它仅仅是一个数据负责数据传送的系统,没有所谓的控制层面和数据层面的说法,不适合处理大规模的网络数据包。最后他得出的结论是:

    OS 的内核不是解决 C10M 问题的办法,恰恰相反 OS 的内核正式导致 C10M 问题的关键所在。

    为什么这么说?基于 OS 内核的数据传输有什么弊端?

    1、中断处理。当网络中大量数据包到来时,会产生频繁的硬件中断请求,这些硬件中断可以打断之前较低优先级的软中断或者系统调用的执行过程,如果这种打断频繁的话,将会产生较高的性能开销。

    2、内存拷贝。正常情况下,一个网络数据包从网卡到应用程序需要经过如下的过程:数据从网卡通过 DMA 等方式传到内核开辟的缓冲区,然后从内核空间拷贝到用户态空间,在 Linux 内核协议栈中,这个耗时操作甚至占到了数据包整个处理流程的 57.1%。

    3、上下文切换。频繁到达的硬件中断和软中断都可能随时抢占系统调用的运行,这会产生大量的上下文切换开销。另外,在基于多线程的服务器设计框架中,线程间的调度也会产生频繁的上下文切换开销,同样,锁竞争的耗能也是一个非常严重的问题。

    4、局部性失效。如今主流的处理器都是多个核心的,这意味着一个数据包的处理可能跨多个 CPU 核心,比如一个数据包可能中断在 cpu0,内核态处理在 cpu1,用户态处理在 cpu2,这样跨多个核心,容易造成 CPU 缓存失效,造成局部性失效。如果是 NUMA 架构,更会造成跨 NUMA 访问内存,性能受到很大影响。

    5、内存管理。传统服务器内存页为 4K,为了提高内存的访问速度,避免 cache miss,可以增加 cache 中映射表的条目,但这又会影响 CPU 的检索效率。

    综合以上问题,可以看出内核本身就是一个非常大的瓶颈所在。那很明显解决方案就是想办法绕过内核。

    解决方案探讨

    针对以上弊端,分别提出以下技术点进行探讨。

    1、控制层和数据层分离。将数据包处理、内存管理、处理器调度等任务转移到用户空间去完成,而内核仅仅负责部分控制指令的处理。这样就不存在上述所说的系统中断、上下文切换、系统调用、系统调度等等问题。

    2、使用多核编程技术代替多线程技术,并设置 CPU 的亲和性,将线程和 CPU 核进行一比一绑定,减少彼此之间调度切换。

    3、针对 NUMA 系统,尽量使 CPU 核使用所在 NUMA 节点的内存,避免跨内存访问。

    4、使用大页内存代替普通的内存,减少 cache-miss。

    5、采用无锁技术解决资源竞争问题。

    经很多前辈先驱的研究,目前业内已经出现了很多优秀的集成了上述技术方案的高性能网络数据处理框架,如 6wind、windriver、netmap、dpdk 等,其中,Intel 的 dpdk 在众多方案脱颖而出,一骑绝尘。

    431521-20180202125234687-1893812869.png

    dpdk 为 Intel 处理器架构下用户空间高效的数据包处理提供了库函数和驱动的支持,它不同于 Linux 系统以通用性设计为目的,而是专注于网络应用中数据包的高性能处理。

    也就是 dpdk 绕过了 Linux 内核协议栈对数据包的处理过程,在用户空间实现了一套数据平面来进行数据包的收发与处理。在内核看来,dpdk 就是一个普通的用户态进程,它的编译、连接和加载方式和普通程序没有什么两样。

    dpdk 的突破

    相对传统的基于内核的网络数据处理,dpdk 对从内核层到用户层的网络数据流程进行了重大突破,我们先看看传统的数据流程和 dpdk 中的网络流程有什么不同。

    传统 Linux 内核网络数据流程:

     

    Copy

    硬件中断--->取包分发至内核线程--->软件中断--->内核线程在协议栈中处理包--->处理完毕通知用户层 用户层收包-->网络层--->逻辑层--->业务层

    dpdk 网络数据流程:

     

    Copy

    硬件中断--->放弃中断流程 用户层通过设备映射取包--->进入用户层协议栈--->逻辑层--->业务层

    下面就具体看看 dpdk 做了哪些突破?

    UIO (用户空间的 I/O 技术)的加持。

    dpdk 能够绕过内核协议栈,本质上是得益于 UIO 技术,通过 UIO 能够拦截中断,并重设中断回调行为,从而绕过内核协议栈后续的处理流程。

    UIO 设备的实现机制其实是对用户空间暴露文件接口,比如当注册一个 UIO 设备 uioX,就会出现文件 /dev/uioX,对该文件的读写就是对设备内存的读写。除此之外,对设备的控制还可以通过 /sys/class/uio 下的各个文件的读写来完成。

    431521-20180202125414796-706128566.png

    内存池技术

    dpdk 在用户空间实现了一套精巧的内存池技术,内核空间和用户空间的内存交互不进行拷贝,只做控制权转移。这样,当收发数据包时,就减少了内存拷贝的开销。

    大页内存管理

    dpdk 实现了一组大页内存分配、使用和释放的 API,上层应用可以很方便使用 API 申请使用大页内存,同时也兼容普通的内存申请。

    无锁环形队列

    dpdk 基于 Linux 内核的无锁环形缓冲 kfifo 实现了自己的一套无锁机制。支持单生产者入列/单消费者出列和多生产者入列/多消费者出列操作,在数据传输的时候,降低性能的同时还能保证数据的同步。

    poll-mode网卡驱动

    DPDK网卡驱动完全抛弃中断模式,基于轮询方式收包,避免了中断开销。

    NUMA

    dpdk 内存分配上通过 proc 提供的内存信息,使 CPU 核心尽量使用靠近其所在节点的内存,避免了跨 NUMA 节点远程访问内存的性能问题。

    CPU 亲和性

    dpdk 利用 CPU 的亲和性将一个线程或多个线程绑定到一个或多个 CPU 上,这样在线程执行过程中,就不会被随意调度,一方面减少了线程间的频繁切换带来的开销,另一方面避免了 CPU 缓存的局部失效性,增加了 CPU 缓存的命中率。

    多核调度框架

    dpdk 基于多核架构,一般会有主从核之分,主核负责完成各个模块的初始化,从核负责具体的业务处理。

    除了上述之外,dpdk 还有很多的技术突破,可以用下面这张图来概之。

    431521-20180202125548812-1308226741.png

    dpdk 的应用

    dpdk 作为优秀的用户空间高性能数据包加速套件,现在已经作为一个“胶水”模块被用在多个网络数据处理方案中,用来提高性能。如下是众多的应用。

    431521-20180202125607359-287540014.png

    数据面(虚拟交换机):

    OVS

    Open vSwitch 是一个多核虚拟交换机平台,支持标准的管理接口和开放可扩展的可编程接口,支持第三方的控制接入。

    https://github.com/openvswitch/ovs

    VPP

    VPP 是 cisco 开源的一个高性能的包处理框架,提供了 交换/路由 功能,在虚拟化环境中,使它可以当做一个虚拟交换机来使用。在一个类 SDN 的处理框架中,它往往充当数据面的角色。经研究表明,VPP 性能要好于 ovs+dpdk 的组合,但它更适用于NFV,适合做特定功能的网络模块。

    https://wiki.fd.io/view/VPP

    Lagopus

    Lagopus 是另一个多核虚拟交换的实现,功能和 OVS 差不多,支持多种网络协议,如 Ethernet,VLAN,QinQ,MAC-in-MAC,MPLS 和 PBB,以及隧道协议,如 GRE,VxLan 和 GTP。

    https://github.com/lagopus/lagopus/blob/master/QUICKSTART.md

    Snabb

    Snabb 是一个简单且快速的数据包处理工具箱。

    https://github.com/SnabbCo/snabbswitch/blob/master/README.md

    数据面(虚拟路由器):

    OPENCONTRAIL

    一个集成了 SDN 控制器的虚拟路由器,现在多用在 OpenStack 中,结合 Neutron 为 OpenStack 提供一站式的网络支持。

    http://www.opencontrail.org/

    CloudRouter

    一个分布式的路由器。

    https://cloudrouter.org/

    用户空间协议栈

    mTCP

    mTCP 是一个针对多核系统的高可扩展性的用户空间 TCP/IP 协议栈。

    https://github.com/eunyoung14/mtcp/blob/master/README

    IwIP

    IwIP 针对 RAM 平台的精简版的 TCP/IP 协议栈实现。

    http://git.savannah.gnu.org/cgit/lwip.git/tree/README

    Seastar

    Seastar 是一个开源的,基于 C++ 11/14 feature,支持高并发和低延迟的异步编程高性能库。

    http://www.seastar-project.org/

    f-stack

    腾讯开源的用户空间协议栈,移植于 FreeBSD协议栈,粘合了 POSIX API,上层应用(协程框架,Nginx,Redis),纯 C 编写,易上手。

    https://github.com/f-stack/f-stack

    总结

    dpdk 绕过了 Linux 内核协议栈,加速数据的处理,用户可以在用户空间定制协议栈,满足自己的应用需求,目前出现了很多基于 dpdk 的高性能网络框架,OVS 和 VPP 是常用的数据面框架,mTCP 和 f-stack 是常用的用户态协议栈。很多大公司都在使用 dpdk 来优化网络性能。

    转载于:https://my.oschina.net/royz2019/blog/3018906

    展开全文
  • 全面分析 MySQL并发控制

    千次阅读 多人点赞 2020-09-30 21:07:30
    本篇虽然题目说:全面分析,但是谁都知道,并发控制是一个多么庞大的概念是吧,本篇主要讲的是:MySQL的锁、存储引擎、事务处理机制。如果不是你期待的,可以省点时间啦;如果是的话,点赞收藏错不了! MyS.

    在这里插入图片描述

    并发控制

    为什么会提出这个话题?不言而喻。

    无论何时,只要有多个查询需要在同一时刻查询数据,都会产生并发问题。

    本篇虽然题目说:全面分析,但是谁都知道,并发控制是一个多么庞大的概念是吧,本篇主要讲的是:MySQL的锁、存储引擎、事务处理机制。如果不是你期待的,可以省点时间啦;如果是的话,点赞收藏错不了!


    MySQL逻辑架构

    在这里插入图片描述

    每个连接都会在mysql服务端产生一个线程(内部通过线程池管理线程),比如一个select语句进入,mysql首先会在查询缓存中查找是否缓存了这个select的结果集,如果没有则继续执行 解析、优化、执行的过程;反之则会直接从缓存中获取结果集。

    我们常规认识的锁是这样的:对于临界资源A,有进程B和进程C需要对其进行访问,为了防止冲突,当某个进程比如说A先到达,它会取得互斥锁,那么在A使用这个资源的时候,B是无法使用这个资源的,它必须等待,直到A释放了锁为止。
    在学校里面老师就是在这么教我们的。

    上面这种互斥锁的方案在实际应用环境中,但并不支持大并发处理,我们来看看一些解决方案:

    读写锁

    读锁:读锁是共享的,多个客户在同一时刻可以同时读取同一个资源,而互不干扰(只要你有读的权限,你就可以当它是透明的)。
    写锁:写锁是排他的,也就是说一个写锁会阻塞其他的写锁和读锁,这是出于安全策略的考虑。

    具体参见:悲观锁和乐观锁(资源在上面)

    在实际的数据库系统中,每时每刻都在发生锁定。大多数时候,MySQL的内部管理都是透明的。

    锁粒度

    在这里插入图片描述

    在这个问题上,我看到了一个非常接地气的比喻:(出处

    为什么要加锁?加锁是为了防止不同的线程访问同一共享资源造成混乱。
    打个比方:人是不同的线程,卫生间是共享资源
    你在上洗手间的时候肯定要把门锁上吧,这就是加锁,只要你在里面,这个卫生间就被锁了,只有你出来之后别人才能用。想象一下如果卫生间的门没有锁会是什么样?

    什么是加锁粒度呢?所谓加锁粒度就是你要锁住的范围是多大。
    比如你在家上卫生间,你只要锁住卫生间就可以了吧,不需要将整个家都锁起来不让家人进门吧,卫生间就是你的加锁粒度。

    怎样才算合理的加锁粒度呢?
    其实卫生间并不只是用来上厕所的,还可以洗澡,洗手。这里就涉及到优化加锁粒度的问题。
    你在卫生间里洗澡,其实别人也可以同时去里面洗手,只要做到隔离起来就可以,如果马桶,浴缸,洗漱台都是隔开相对独立的,实际上卫生间可以同时给三个人使用,
    当然三个人做的事儿不能一样。这样就细化了加锁粒度,你在洗澡的时候只要关上浴室的门,别人还是可以进去洗手的。如果当初设计卫生间的时候没有将不同的功能区域划分
    隔离开,就不能实现卫生间资源的最大化使用。这就是设计架构的重要性。

    表锁(table lock)

    表锁是MySQL中最基本的锁策略,并且是开销最小的策略。它会锁定整张表,这是什么意思我就不多说了啊。
    但是吧,放对了位置,表锁也可以有不错的体现,比方说服务器会为诸如ALTER TABLE之类的语句使用表锁而忽略存储引擎的锁机制。

    行级锁

    行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。

    InnoDB等比较常用的存储引擎中都实现了行级锁。

    页级锁

    页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。


    事务

    MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既需要删除人员的基本资料,也要删除和该人员相关的信息,这样,这些数据库操作语句就构成一个事务!

    在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
    事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
    事务用来管理 insert,update,delete 语句
    

    事务的四个特性(ACID)

    原子性:一个事务是不可再分割的整体,要么都执行要么都不执行
    一致性:一个事务的执行不能破坏数据库数据的完整性和一致性
    隔离性:一个事务不受其它事务的干扰,多个事务是互相隔离的
    持久性:一个事务一旦提交了,则永久的持久化到本地
    

    就像锁粒度的升级会增加系统开销一样,这种事务处理过程中额外的安全性也会需要数据库系统做更多的额外工作。一个实现了ACID的数据库,相比没有实现ACID的数据库,通常会需要更多的CPU处理能力、更大的内存和更多的磁盘空间,这也正是MySQL的存储引擎可以发挥优势的地方,用户可以根据自身需要来选定存储引擎。

    隔离级别

    数据库为了压制丢失更新,提出了4类隔离级别[在application配置文件中声明]。
    数据库现在的技术完全有办法避免丢失更新,但是这样做的代价是要付出锁的代价。一旦用了过多的锁,出现商品抢购这类功能的时候,很多线程都会被挂起和恢复,因为使用了锁之后,一个时刻只能有一个线程访问数据,这样当多个线程访问时,就会很慢,而且过多的锁会引发宕机,大部分线程被挂起,等待持有锁事务的完成。

    隔离性其实比想象的要复杂,我们来看看:
    (简单介绍几种)

    READ UNCOMMITTED(读取未提交内容)

    在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

    Read Committed(读取提交内容)

    这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

    Repeatable Read(可重读)

    这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

    Serializable(可串行化)

    这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。


    在这里插入图片描述

    要是再深挖下去,那得是专门的研究员们做的事情了吧,比方说MySQL是如何保证可重复读的实现,比方说幻读是怎么被咔嚓掉的之类的。

    后面我看看能不能查到些好的资料贴上来。


    死锁

    为什么这个死锁不放在上面“锁”的模块里面讲呢?木有事务,谈什么死锁。

    死锁的基本概念我也不啰嗦了,为了解决死锁的问题,数据库系统实现了各种死锁检测和超时机制。InnoDB检测死锁的本事就不错,它会抓出死锁的循环依赖,并且抛出一个错误。
    InnoDB目前处理死锁的方法:将持有最少行级排他锁的事务进行回滚。
    锁的行为和存储引擎是密不可分的,同样的事务执行顺序,有的存储引擎就会死锁,有的就不会。。


    事务日志

    我觉得可以参考一下redis的日志:全面分析redis持久化机制

    吃出不过多赘述,其他篇关于MySQL的博客里已经讲得够多了。
    如果觉得确实想要了解一下,这里倒是有一篇写得很全面的:详细分析MySQL事务日志

    想赶紧进入存储引擎的模块。


    存储引擎

    我先不说话,我先放张图,你品,你细品
    在这里插入图片描述

    InnoDB

    我还是不说话,我放图:
    在这里插入图片描述

    InnoDB后台有多个不同的线程,用来负责不同的任务。

    InnoDB 存储引擎是基于磁盘存储的,也就是说数据都是存储在磁盘上的,由于 CPU 速度和磁盘速度之间的鸿沟, InnoDB 引擎使用缓冲池技术来提高数据库的整体性能。缓冲池简单来说就是一块内存区域.在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中,下一次读取相同的页时,首先判断该页是不是在缓冲池中,若在,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。对于数据库中页的修改操作,首先修改在缓冲池中页,然后再以一定的频率刷新到磁盘,并不是每次页发生改变就刷新回磁盘。

    在InnoDB中,缓冲池中的页大小默认为16KB。


    InnoDB 给 MySQL 提供了具有事务(transaction)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)、多版本并发控制(multi-versioned concurrency control)的事务安全(transaction-safe (ACID compliant))型表。InnoDB 提供了行级锁(locking on row level),提供与 Oracle 类似的不加锁读取(non-locking read in SELECTs)。InnoDB锁定在行级并且也在SELECT语句提供一个Oracle风格一致的非锁定读。这些特色增加了多用户部署和性能。没有在InnoDB中扩大锁定的需要,因为在InnoDB中行级锁定适合非常小的空间。InnoDB也支持FOREIGN KEY强制。在SQL查询中,你可以自由地将InnoDB类型的表与其它MySQL的表的类型混合起来,甚至在同一个查询中也可以混合。这些特性均提高了多用户并发操作的性能表现。在InnoDB表中不需要扩大锁定(lock escalation),因为 InnoDB 的行级锁定(row level locks)适宜非常小的空间。InnoDB 是 MySQL 上第一个提供外键约束(FOREIGN KEY constraints)的表引擎。


    InnoDB采用MVCC来支持高并发,并且实现了四个标准隔离级别。其默认隔离级别是REPEATABLE READ(可重复读),并且通过间隙锁策略防止幻读的出现,间隙锁使得InnoDB不仅仅对锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。


    不好搞,建议:

    推荐:官方文档:InnoDB事务模型和锁
    翻译版:https://www.cnblogs.com/EmptyRabbit/p/13688580.html

    建议:更深入的了解一下InnoDB的MVCC架构。


    其他的引擎嘛,其实我只喜欢InnoDB。。。
    叶公好龙哈哈哈

    其他存储引擎

    MyISAM

    特性:

    1. 加锁与并发:表锁,读写锁
    2. 修复:可手动或自动执行检查和修复工作,就是慢了点。
    3. 索引特性:支持全文索引
    4. 性能:设计简单,在某些情况下性能很好,嗯,某些情况下。

    CSV引擎

    CSV引擎可以将普通的CSV文件作为MySQL的表来处理,但这种表并不支持索引。CSV引擎可以在数据库运行的时候拷入或拷出文件,因此CSV作为一种数据交换的机制,非常有用。

    Memory引擎

    如果需要快速的访问数据,并且这些数据不会被修改,重启后丢失也没有关系,那么可以使用Memory表,因为所有数据都保存在内存中,不需要进行磁盘IO。
    Memory表的结构在重启以后还会保留,但数据会丢失。


    先到这里啦,我努力了,接下来就看各位的努力了,三连走起!!!

    在这里插入图片描述

    展开全文
  • 一辆汽车加满油后可以行驶N千米。旅途中有若干个加油站。指出若要使沿途的加油次数最少,设计一个有 效的算法,指出应在那些加油站停靠加油。...问题分析(前提行驶前车里加满油)对于这个问题我们...

    一辆汽车加满油后可以行驶N千米。旅途中有若干个加油站。指出若要使沿途的加油次数最少,设计一个有 效的算法,指出应在那些加油站停靠加油。给出N,并以数组的形式给出加油站的个数及相邻距离,指出若要使沿途的加油次数最少,设计一个有效的算法,指出应 在那些加油站停靠加油。要求:算法执行的速度越快越好。


    文章来源:C 技术网原创文章版权所有,未经授权,禁止转载。

    问题分析(前提行驶前车里加满油)
    对于这个问题我们有以下几种情况:设加油次数为k,每个加油站间距离为a[i];i=0,1,2,3……n
    1.始点到终点的距离小于N,则加油次数k=0;
    2.始点到终点的距离大于N,
    A 加油站间的距离相等,即a[i]=a[j]=L=N,则加油次数最少k=n;
    B 加油站间的距离相等,即a[i]=a[j]=L>N,则不可能到达终点;
    C 加油站间的距离相等,即a[i]=a[j]=L<N,则加油次数k=n/N(n%N==0)或k=[n/N] 1(n%N!=0);
    D 加油站间的距离不相等,即a[i]!=a[j],则加油次数k通过以下算法求解。

    算法描述
    贪心算法的基本思想
    该题目求加油最少次数,即求最优解的问题,可分成几个步骤,一般来说,每个步骤的最优解不一定是整个问题的最优解,然而对于有些问题,局部贪心可以得到 全局的最优解。贪心算法将问题的求解过程看作是一系列选择,从问题的某一个初始解出发,向给定目标推进。推进的每一阶段不是依据某一个固定的递推式,而是 在每一个阶段都看上去是一个最优的决策(在一定的标准下)。不断地将问题实例归纳为更小的相似的子问题,并期望做出的局部最优的选择产生一个全局得最优 解。


    由于汽车是由始向终点方向开的,我们最大的麻烦就是不知道在哪个加油站加油可以使我们既可以到达终点又可以使我们加油次数最少。


    提出问题是解决的开始.为了着手解决遇到的困难,取得最优方案。我们可以假设不到万不得已我们不加油,即除非我们油箱里的油不足以开到下一个加油站,我 们才加一次油。在局部找到一个最优的解。却每加一次油我们可以看作是一个新的起点,用相同的递归方法进行下去。最终将各个阶段的最优解合并为原问题的解得 到我们原问题的求解。


    include<math.h> 

    include<studio.h> 

    int add(int b[ ],int m,int n) { //求一个从m到n的数列的和 

    int sb; 

    for(int i=m;i<n;i ) 

    sb =b[i]; 

    return sb; 

    }

     int Tanxin(int a[n], int N) //a[n]表示加油站的个数,N为加满油能行驶的最远距离 

    {

     int b[n]; //若在a[i]加油站加油,则b[i]为1,否则为0 int m=0; if(a[i]>N) return ERROR; //如果某相邻的两个加油站间的距离大于N,则不能到达终点 

    if(add(a[i], 0, n)<N) { //如果这段距离小于N,则不需要加油 

    b[i]=0; return add(b[i],0,n);

     } 

    if(a[i]==a[j]&&a[i]==N) { //如果每相邻的两个加油站间的距离都是N,则每个加油站都需要加油 

    b[i]=1; 

    return add(b[i],0,n); 

    if(a[i]==a[j]&&a[i]<N) { //如果每相邻的两个加油站间的距离相等且都小于N 

    if( add(a[i],m,k) < N && add(a[i],m,k 1) > N ) { 

    b[k]=1; 

    m =k; 

    return add(b[i],0,n); 

    if(a[i]!=a[j]) { //如果每相邻的两个加油站间的距离不相等且都小于N 

    if( add(a[i],m,k) < N && add(a[i],m,k 1) > N ) { 

    b[k]=1; 

    m =k; 

    return add(b[i],0,n); 

    viod main { 

    int a; 

    scanf('%d',a); 

    scanf('/n'); 

    scanf('/d',&N); 

    Tanxin(a[ ],0,n); 

    }

    贪心算法的适用的问题

    贪心算法适用的问题必须满足两个属性:
    (1) 贪心性质:整体的最优解可通过一系列局部最优解达到,并且每次的选择可以依赖以前做出的选择,但不能依赖于以后的选择。
    (2) 最优子结构:问题的整体最优解包含着它的子问题的最优解。
    贪心算法的基本步骤
    (1) 分解:将原问题分解为若干相互独立的阶段。
    (2) 解决:对于每一个阶段求局部的最优解。
    (3) 合并:将各个阶段的解合并为原问题的解。


    文章来源:C 技术网原创文章版权所有,未经授权,禁止转载。



    http://www.360doc.com/content/16/0611/11/10603700_566714103.shtml

    展开全文
  • EventBus 使用(全面分析,细节提醒)

    万次阅读 多人点赞 2018-12-08 10:58:48
    EventBus使用 概述 关于 EventBus 在开发中经常会...因此准备对EventBus进行一个深入,全面的理解,消除模糊,片面感,让以后在使用这个库的时候,有更好的掌握和使用。并记录下来,方便以后查阅。关于EventBus会分...

    EventBus使用

    概述

    关于 EventBus 在开发中经常会选择使用它来进行模块间通信、解耦。平常使用这个库只是很浅显的操作三部曲,register,post,unregister。来达到开发目的。始终有种不明确,模糊的操作感。因此准备对EventBus进行一个深入,全面的理解,消除模糊,片面感,让以后在使用这个库的时候,有更好的掌握和使用。并记录下来,方便以后查阅。关于EventBus会分两章进行记录,本篇文章,是对EventBus的使用做一个全面的介绍,另一篇文章则会对EventBus库的源码进行分析,看看他的实现原理是什么样的。

    关于

    EventBus 是一个开源库,它利用发布/订阅者者模式来对项目进行解耦。它可以利用很少的代码,来实现多组件间通信。android的组件间通信,我们不由得会想到handler消息机制和广播机制,通过它们也可以进行通信,但是使用它们进行通信,代码量多,组件间容易产生耦合引用。关于EventBus的工作模式,这里引用一张官方图帮助理解。

    在这里插入图片描述

    上面这张图还是很好理解的,Publisher(发布者)通过post()方法,把Event事件发布出去,Subscriber(订阅者)在onEvent()方法中接收事件,可能现在你对Publisher,Subscriber, onEvent()这几个词还是不理解,没关系,别着急,放下看慢慢的你就懂了。

    为什么会选择使用EventBus来做通信?

    • 简化了组件间交流的方式
    • 对事件通信双方进行解耦
    • 可以灵活方便的指定工作线程,通过ThreadMode
    • 速度快,性能好
    • 库比较小,不占内存
    • 使用这个库的app多,有权威性
    • 功能多,使用方便

    现在,我们就来体验EventBus的强大之处吧。

    使用

    导入项目

    在build.gradle文件中导入EventBus库。

        implementation 'org.greenrobot:eventbus:3.1.1'
    
    

    在项目的混淆文件中,加入EventBus 的混淆规则,这个千万别忘了,不然会出现,debug版本测试OK,release版本subscriber 收不到消息等诡异问题。

    -keepattributes *Annotation*
    -keepclassmembers class * {
        @org.greenrobot.eventbus.Subscribe <methods>;
    }
    -keep enum org.greenrobot.eventbus.ThreadMode { *; }
     
    # Only required if you use AsyncExecutor
    -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
        <init>(java.lang.Throwable);
    }
    

    使用方式

    EventBus库中最重要的三个点,分别是subscriber(订阅者),事件(消息),publisher(发布者)。主要理解这三者的关系即可。

    subscriber ——> EventBus 的register方法,传入的object对象

    事件(Event)——> EventBus 的post方法,传入的类型。

    publisher(发布者)——> EventBus的post方法。


    1.创建一个事件类型,消息事件类型可以是string,int等常见类,也可以是自己自定义一个事件类,方便管理。这边演示,创建了一个EventMessage事件类。

    public class EventMessage {
    
        private int type;
        private String message;
    
        public EventMessage(int type, String message) {
            this.type = type;
            this.message = message;
        }
    
        @Override
        public String toString() {
    
            return "type="+type+"--message= "+message;
        }
    
        public int getType() {
            return type;
        }
    
        public void setType(int type) {
            this.type = type;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    

    2.在需要订阅事件的模块中,注册eventbus。

    MainActivity.java

    @Override
        protected void onStart() {
            super.onStart();
            EventBus.getDefault().register(this);
        }
    
        @Override
        protected void onStop() {
            super.onStop();
    
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            EventBus.getDefault().unregister(this);
        }
    

    关于EventBus的注册问题,说几点。

    1. 注册完了,在不用的时候千万别忘了unregister。
    2. 不能重复注册。注册之后,没有unregister,然后又注册了一次。
    3. register与unregister的时间根据实际需求来把控,官方的例子是在onStart()回调方法进行注册,onStop()回调方法进行unregister(),这边根据需求做了改动。

    在需要接受事件的类中进行好register之后,需要在该类中创建一个方法来接收事件消息。

    @Subscribe(threadMode = ThreadMode.MAIN)
        public void onReceiveMsg(EventMessage message) {
            Log.e(TAG, "onReceiveMsg: " + message.toString());
        }
    

    创建的这个方法是有要求的。要求有如下几点。

    1. 该方法有且只有一个参数。
    2. 该方法必须是public修饰符修饰,不能用static关键字修饰,不能是抽象的(abstract)
    3. 该方法需要用@Subscribe注解进行修饰。

    关于Subscribe注解的介绍,将在后面结合实例进行讲解。

    3.在需要发送事件的地方,调用EventBus的post(Object event),postSticky(Object event)来通知订阅者。

    SecondActivity.java

    private View.OnClickListener mSendListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "onClick: " );
                EventMessage msg = new EventMessage(1,"Hello MainActivity");
                EventBus.getDefault().post(msg);
            }
        };
    

    注意,该例子中,我使用了,EventBus.getDefault()方法,该方法会获取一个单例。所以才可以随时使用,如果不是用这种单例模式,需要想办法把订阅者(Subscriber)注册时用的EventBus的引用传给需要发送事件的模块中,简而言之就是Subscriber用的eventbus 和post方法需要的eventbus需要是同一个eventbus。

    我们跑下代码测试一下。结果如下。

    在这里插入图片描述

    发现使我们想要的结果。那现在关于EventBus的基本使用,就明白了,主要就是三个部分,一个部分是Subscriber,需要在Subscriber类中进行register和unregister操作。一部分是在Subscriber中需要创建一个方法来接收事件信息,最后一部分就是在需要发送事件的环境使用post方法来发送事件信息。这三部分中所用到的eventBus实例得要是同一个实例。


    @Subscribe 注解介绍

    上面的接收事件的方法中,我们提到,必须要加入@Subscriber注解才可以,这其中的因果我们将在下篇文章进行分析,我们现在所要说的是Subscribe注解的用法。

    @Subscribe是EventBus自定义的一种注解,他可接收三个参数。ThreadMode、boolean sticky、int priority。

    所以上面的接收Event方法的代码,完整版的可以这样写:

    @Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1)
        public void onReceiveMsg(EventMessage message) {
            Log.e(TAG, "onReceiveMsg: " + message.toString());
        }
    

    这三个参数可以根据需要选择是否使用。

    threadMode 是用来决定onReceiveMsg将在哪种线程环境下被调用。EvenBus一共有5种Thread mode。

    POSTING :这是EventBus的默认模式,表示post事件是什么线程,onReceiveMsg接收事件方法就在同样的线程环境中执行代码。例如:

    订阅处

    @Subscribe()
        public void onReceiveMsg(EventMessage message) {
            Log.e(TAG, "onReceiveMsg: " + message.toString());
            Log.e(TAG, "onReceiveMsg: current thread name ="+Thread.currentThread().getName() );
        }
    

    发布处

    private View.OnClickListener mSendListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "onClick: " );
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String name = Thread.currentThread().getName();
                        Log.e(TAG, "run: thread  name = "+name );
                        EventMessage msg = new EventMessage(1,"Hello MainActivity");
                        EventBus.getDefault().post(msg);
    
                    }
                }).start();
            }
        };
    

    结果:

    在这里插入图片描述

    我们看到,他们所处的线程环境是一样的。

    这种模式,适合使用在执行简单任务的情况下,不需要复杂运算,因为这种模式不需要做线程切换的判断逻辑,直接分发至相同的线程环境,速度快,耗时少。

    MAIN:关于MAIN 这种线程模式,可以和MAIN_ORDERED一起讨论,他们都是表示,无论事件发布在什么线程,事件接收都是在主线程中执行。那MAIN模式和MAIN_ORDERED模式的区别在哪里呢?

    区别在于,对于MAIN 模式,如果事件发布者post事件也是在主线程的话,会阻塞post事件所在的线程。意思就是连续post多个事件,如果接收事件方法执行完,才能post下一个事件。

    post(1) ——> onReceiveMsg(1) ———>post(2)———>onReceiveMsg(2)———>post(3)————>onReceiveMsg(3)

    如果事件发布者post事件不在主线程的话,连续post多个事件,同是在主线程是接收事件是耗时操作的话,执行的流程会是这样的。是非阻塞的(non-blocking)

    post(1)——>post(2)——>psot(3)———>onReceiveMsg(3)

    或者

    post(1)——>post(2)——>psot(3)———>onReceiveMsg(2)——>onReceiveMsg(3)

    那对于MAIN_ORDERED模式无论事件发布者post在什么线程环境,他的执行流程是都非阻塞的(non-blocking),和MAIN模式 下,post环境不是主线程的执行流程一样。

    BACKGROUND:该模式下的时间发布者post线程环境与事件接收onReceiveMsg方法的线程环境关系如下:

    post发布环境是主线程的话,事件接收处理的环境是一个子线程。

    post发布环境是子线程的话,事件接收处理环境和post发布环境一样。

    ASYNC:该模式表示,无论post环境是什么线程,事件接收处理环境都是子线程。

    以上就是EventBus五种线程模式的解读。上面说了Subscriber注解的ThreadMode参数的含义。接着我们说另外两种参数的含义。

    sticky

    sticky是一个boolean型的参数,默认值是false,表示不启用sticky特性。那么sticky特性是什么呢?我们之前说的EventBus事件传递的例子的时候,我们都是先对订阅者(Subscriber)进行先注册的,然后再post事件的。那sticky的作用是在先post事件,后对订阅者注册这种开发场景的支持的。

    举个例子:

     private View.OnClickListener mGoListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "onClick: post");
                EventMessage message = new EventMessage(233, "post message before");
                EventBus.getDefault().postSticky(message);
            }
        };
    
        private View.OnClickListener mRegisterListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "onClick: start register" );
                EventBus.getDefault().register(MainActivity.this);
            }
        };
    

    我首先会点击触发go事件,通过postSticky()发送一个事件,然后再通过点击触发register事件,对Subscriber进行注册。结果log如下:

    2018-12-08 10:40:54.444 9129-9129/com.example.yumryang.eventbusdemo E/MainActivity: onClick: post
    2018-12-08 10:40:57.365 9129-9129/com.example.yumryang.eventbusdemo E/MainActivity: onClick: start register
    2018-12-08 10:40:57.369 9129-9129/com.example.yumryang.eventbusdemo E/MainActivity: onReceiveMsg: type=233--message= post message before
    2018-12-08 10:40:57.369 9129-9129/com.example.yumryang.eventbusdemo E/MainActivity: onReceiveMsg: current thread name =main
    

    priority

    该参数是int型,默认值是0,比较好理解,就像他的参数名所表示的那样,优先级。值越高,越先接收到事件,不过这里要注意一个问题,那就是优先级的比较前提是在post事件发布,onReceiveMsg事件接收处理这两方的线程环境相同的前提下,才有意义。同是与priority相配合使用的一个方法是cancelEventDelivery.关于它们的使用就不在演示,比较简单。

    结尾

    EventBus的入门篇,大致就说这些, 可能还有些注意问题,暂时没想起来,等下一篇,关于EventBus源码分析的时候到时候遇到了,再提出来吧。


    第二篇文章出来啦!

    EventBus源码分析,看这一篇就够了!


    欢迎扫描下方二维码关注我的公众号,一起加油!
    在这里插入图片描述

    展开全文
  • 爬虫抓包问题全面分析

    千次阅读 2021-09-11 21:07:10
    下面将从各个方面讲解app抓包技巧, 讲解会遇到的各个问题及其相应的解决办法 网络七层模型 既然说到网路抓包,那么我们从网络七层模型开始说起,其实pc端爬虫往往只会关注应用层的数据抓包,但是很多大厂 的app...
  • 借助性能优化工具分析解决问题 性能优化指标 性能问题分类 1、渲染问题:过度绘制、布局冗杂 2、内存问题:内存浪费(内存管理)、内存泄漏 3、功耗问题:耗电 性能优化原则和方法 1、性能优化原则 坚持...
  • 背景: 紧随上一篇博文,通过利用RawCap.exe和WireShark等抓包工具从数据流最底层角度来分析一下DICOM3.0标准中的网络通讯服务,并且查看分析了DCMTK和fo...
  • 往往我们在配置完路由器将内部成功访问到外部互联网以及外部...下面将通过实验来分析出现这种问题的原因。 实验拓扑: 实验配置:  实验各接口配置如上图,重点来看router1(简R1)配置,如下 R1#sh run interfac
  • 学会发现问题--提出问题--分析问题--解决问题

    万次阅读 热门讨论 2018-01-23 18:01:07
     今天下午做了一次英语快照,第一次做总会出现很多问题 问题:  1、摄像问题。时间过去一半了才想起来没有摄像机,于是我们又在摄像机上面浪费了5分钟左右的时间,更可惜的是大家都在等待着,所以因为我...
  • 关于idea对javaweb项目的编译打包配置基于maven的javaweb目录结构idea javaweb的编译设置说明配置idea编译javaweb项目配置idea编译并且生成产品artifact将artifact发布到外部的tomcat关于Configuration ... 分析...
  • # 处理地名数据,解决坐标文件中找不到地名的问题 def handle(cities): # print(len(cities), len(set(cities))) # 获取坐标文件中所有地名 data = None with open( '/Users/wangbo/PycharmProjects/python-...
  • 在实际课题中,为了全面分析问题,往往提出很多与此有关的变量(或因素),因为每个变量都在不同程度上反映这个课题的某些信息。主成分分析首先是由K.皮尔森(Karl Pearson)对非随机变量引入的,尔后H.霍特林
  • 数学建模问题分析

    千次阅读 2020-04-04 18:49:55
    对数据补全和基本趋势分析(根据某个未知函数(或已知但难于求解的函数)的几个已知数据点求出变化规律和特征相似的近似曲线的过程。) 插值:求过已知有限个数据点的近似函数。 拟合:已知有限个数据点,求近似函数...
  • 现在市场上的机子都搞全面屏,我们开发出来的应用在18+:9的全面屏模式会出现上下方黑条问题,解决如下 AndroidManifest.xml中application里添加代码: <!-- 解决miui等全面屏模式下方黑条问题,value填大于2...
  • 一款全面高效的日志分析工具,操作更简单  Eventlog Analyzer是用来分析和审计系统及事件日志的管理软件,能够对全网范围内的主机、服务器、网络设备、数据库以及各种应用服务系统等产生的日志,进行全面收集和细致...
  • Android CTS 问题分析

    千次阅读 2017-04-18 21:24:17
    为保证开发的应用在所有兼容Android的设备上正常运行,并保证一致的用户体验,Google制定了CTS(Compatibility Test Suite)来确保设备运行的Android系统全面兼容Android规范,Google也提供了一份兼容性标准文档...
  • 全面教你如何建立数据分析的思维框架

    千次阅读 多人点赞 2017-07-07 09:10:04
    目前,还有一些人不会建立数据分析的思维框架,那么今天课课家,就一步一步的教大家怎么建立,大神路过还请绕道,当然还可以交流一下。有需要的小伙伴,可以参考一下。  曾经有人问过我,什么是数据分析思维?...
  • 主成分分析(PCA)原理详解

    万次阅读 多人点赞 2018-06-09 15:08:25
    “微信公众号”本文同步更新在我的微信公众号里,地址:https://mp.weixin.qq.com/s/Xt1vLQfB20rTmtLjiLsmww本文同步更新在我的知乎专栏里面:主成分分析(PCA)原理详解 - Microstrong的文章 - 知乎...
  • 文章目录一、算法介绍二、适用问题三、算法总结四、应用场景举例五、SPSS操作1.归一化2. 主成分分析六、实际... 在实际课题中,为了全面分析问题,往往提出很多与此有关的变量(或因素),因为每个变量都在不同程度
  • 很多同学表示:从0到1的文章很多,可面对复杂问题,该怎么搭建数据分析思路呢?首先,“复杂”一词在不同等级的数据分析师里含义不同。对小白而言,领导传达命令的时候,有“模型”俩字的就是复杂问...
  • 在竞争加剧的背景下,企业面临诸多问题:如何生存?如何打造企业的竞争优势?企业成本为什么居高不下?企业利润空间越来越小,企业如何提升利润空间?企业如何控制成本?如何减少损失?如何消除浪费……人工智能赛博...
  • 分析问题的黄金法则——MECE分析法

    千次阅读 2007-12-26 13:13:00
    什么是MECE分析法? MECE,是Mutually Exclusive Collectively Exhaustive,中文意思是“相互独立,完全穷尽”。 也就是对于一个重大的议题,能够做到不重叠、不遗漏的分类,而且能够借此有效把握问题的核心,并...
  • 数据分析面试中的业务问题

    万次阅读 多人点赞 2019-09-03 23:19:36
    首先,这类估算问题会经常出现在数据分析、产品、咨询类岗位,统称为费米问题分析这类问题可以 分别从两个角度展开。 根据情况,可以采用 Top down bottom up 法则 ,即 先从宏观层面, 自上而下推,再由某个点...
  • 在实际课题中,为了全面分析问题,往往提出很多与此有关的变量(或因素),因为每个变量都在不同程度上反映这个课题的某些信息。 主成分:由原始指标综合形成的几个新指标。依据主成分所含信息量的大小成为第一...
  • 在实际课题中,为了全面分析问题,往往提出很多与此有关的变量(或因素),因为每个变量都在不同程度上反映这个课题的某些信息。主成分分析首先是由K.皮尔森对非随机变量引入的,尔后H.霍特林将此方法推广到随机向量...
  • 六项思考帽:全面问题的思维框架 六顶思考帽通过六种形象的颜色,激发人的想象,提醒人们从不同的角度思考问题,形成全面问题的思维框架。 白色思考帽:中立而客观的白色,提醒人在讨论问题之前检察数字和事实...
  • 响应时间过长问题分析

    万次阅读 2019-07-18 16:29:49
    不管是性能测试中,还是生产环境中,经常会遇到响应时间过长的问题。 响应时间是性能评估的一个重要指标,会对最终用户产生直接影响,一个产品是快是慢,响应时间是最直观的感受。 因此面对响应时间长的问题,一定想...
  • C/C++程序CPU问题分析

    千次阅读 2017-11-11 19:26:34
    转载地址:...本次我们收集了14个CPU类的问题,和大家一起分析下这些问题的种类和原因。另外,对于C/C++程序而言,目前已经有了很多CPU问题定位的工具,本文也会进行比较分析。CPU问题分析  程序
  • 在开发一个完整的web项目时,总是会遇到各种各样的中文乱码问题,例如页面显示乱码,表单提交乱码,数据库存储乱码等 等,虽然目前也能找到各种各样的解决方案,但是大部分都没有总结全面。(我也遇到了中文乱码问题 ...
  • AHP(层次分析法)的全面讲解及python实现

    千次阅读 多人点赞 2020-05-09 13:58:30
    一、层次分析法的使用流程: 1. 建立层次结构模型 首先绘出层次结构图,正常三层是比较常见的:决策的目标、考虑的决策准则因素和决策对象。按它们之间的相互关系分为最高层、中间层和最低层(如下图是四层结构的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 319,866
精华内容 127,946
关键字:

如何全面分析问题