读锁操作方法、写锁操作方法及***
技术领域
本申请涉及计算机***结构技术领域,特别涉及一种读锁操作方法、写锁操作方法及***。
背景技术
线程(thread),也被称为轻量级进程(Lightweight Process,LWP),是进程中某个单一顺序的控制流,并作为程序执行的最小单元。在引入线程的操作***中,通常把进程作为分配资源的基本单位,把线程作为独立运行和独立调度的基本单位。线程可以并发执行,例如一个进程中的多个线程可以并发执行。不同进程中的线程也能并发执行。特别的,在多计算核心的计算机***中,例如具有多个CPU核心的计算机***中,不同核心的线程也可以并发执行。
多个线程并发执行时,往往需要访问同一数据。从该被访问的数据看,该数据共享给不同线程。在多个线程访问该共享数据时,需要保证该共享数据的完整性。例如,不能两个线程同时修改共享数据;一个线程不能读到修改了一半的共享数据。经典的方式是使用锁(Lock)机制。例如,在线程对数据进行读操作的过程中为该数据加上“读锁”,在线程对数据进行写操作的过程中为该数据加上“写锁”。进程对一个数据进行读操作前,先对该数据加读锁,读操作执行毕之后,再解读锁。类似的,进程对一个数据进行写操作前,先对该数据加写锁,写操作执行毕之后,再解写锁。通常用read_ref作为读操作线程的引用计数,用writer_ID表示写操作线程的ID。
对于不同线程对同一数据执行的都是读操作来说,可以加多次读锁。例如,线程1对一数据要执行读操作,则在执行读操作之前,对该数据加读锁,具体是将read_ref的值加1(例如read_ref的数据类型为整形,且初始值为0),之后对该数据进行读取。在读取的过程中,线程2也要对同一数据执行读操作,则将read_ref的值加1,并对该数据进行读取。则此时read_ref的值为2。线程1的读操作执行完毕之后,将read_ref的值减1,并解读锁。此时,read_ref的值为1。之后,线程2对该数据的读操作执行完毕,将read_ref的值减1,并解读锁。此时,read_ref的值为0。对同一数据的读锁可以重复加,因此,读锁之间具有共享性。
对于不同线程对同一数据执行的都是写操作来说,只能加一次写锁。例如,线程1对一数据要执行写操作,则在执行写操作之前,对该数据加写锁,具体是将writer_ID的值更新为线程1的ID(例如writer_ID的数据类型为整形,且初始值为0;任一线程的ID不为0),之后对该数据进行写操作。在写的过程中,线程2也要对同一数据执行写操作,但因writer_ID的值此时不为0,因此线程2不能加写锁,也不能对该数据进行写操作。线程1的写操作执行完毕之后,解写锁,即将writer_ID的值更新为0。线程2在前次加写锁失败并等待一段时间后,得知writer_ID的值此时为0,可以加写锁。之后,writer_ID的值更新为线程2的ID,之后对该数据进行写操作。线程2的写操作执行完毕之后,解写锁,即将writer_ID的值更新为0。可见,对同一数据的写锁不可以重复加,因此,写锁之间具有互斥性。
此外,写锁和读锁也是互斥的,即在任一时刻,对同一数据加了读锁就不能再加写锁,加了写锁也不能再加读锁。这样,某一线程在对一数据进行读操作前,需要检查该数据的writer_ID值是否为0。如果为0,才可以进行读操作;如果不为0,需要等待writer_ID值变为0。类似的,某一线程在对一数据进行写操作前,需要检查该数据的read_ref值是否为0。如果为0,才可以进行写操作;如果不为0,需要等待read_ref值变为0。实际上,对于加读锁,更多情况下,为了进一步避免在检查writer_ID值的操作与相应的读操作之间有另一线程同时为写操作而进行的加写锁操作,即为了避免该种情况下导致的冲突检测失效,将read_ref的值加1之后,还将再次检查此时的该writer_ID值是否为0。如果不为0,才执行读操作。类似的,对于加写锁,更多情况下,为了进一步避免在检查read_ref值的操作与相应的写操作之间有另一线程同时为读操作而进行的加读锁操作,即为了避免该种情况下导致的冲突检测失效,将writer_ID的值更新为写线程的ID之后,还将再次检查此时的该read_ref值是否为0。如果不为0,才执行写操作。
上述线程对read_ref值、writer_ID的值更改,属于原子操作。原子操作一般是CPU提供的指令,具有不可分割性。一个线程在执行一个原子操作时,不会被别的线程打断,不会切换到其他线程。换句话说,这种原子操作一旦开始,就一直运行到该操作结束。
在实现本申请过程中,发明人发现现有技术中至少存在如下问题:
在多计算核心的计算机***中,不同核心的线程可能会对同一数据执行读、写操作。特别是经常会出现一段时间内对同一数据执行大量读操作,而没有写操作的情况。每个核心一般都对应一个缓存(cache)。每个核心在其对应的缓存中维护一个read_ref值。并且,按照现有技术中的实现方式,每个核心对应的缓存中的read_ref值要保持一致。这样,对于多计算核心的计算机***来说,一个核心对应的缓存中的read_ref值一旦发生改变之后,要与其它核心通信以通知这一改变。其它核心收到通知后更新自身对应缓存中的read_ref值。
这样,现有技术中的这一方式,导致不同核心的多个线程对同一数据进行读操作时,由于核心之间的通信要花费一定时间,每个核心对应缓存中更改read_ref值的原子操作都需要花费一定时间,因而执行效率较低。
发明内容
本申请实施例的目的是提供一种读锁操作方法、写锁操作方法及***,以提高执行效率。
为解决上述技术问题,本申请实施例提供一种读锁操作方法、写锁操作方法及***是这样实现的:
一种读锁操作方法,包括:
设置每个核心对应的私有引用计数;
在不同核心的线程对同一数据进行读取过程中,以不同核心对应的私有引用计数进行加读锁、解读锁操作。
一种读锁操作***,包括数据单元,一缓存单元,第二缓存单元,第一计算核心和第二计算核心,其中,
数据单元,用于存储数据;
第一缓存单元,用于保存为第一计算核心的分配的第一私有引用计数;
第二缓存单元,用于保存为第二计算核心的分配的第二私有引用计数;
第一计算核心和第二计算核心,用于对所述数据单元中的同一数据进行读取;且,
第一计算核心的线程对所述数据进行读取过程中,以第一核心对应的私有引用计数进行加读锁、解读锁操作;
第二计算核心的线程对所述数据进行读取过程中,以第二核心对应的私有引用计数进行加读锁、解读锁操作。
一种写锁操作方法,包括:
对数据执行写操作前,判断所有计算核心中是否存在对该数据的读操作过程;
对数据执行写操作前,判断所述数据是否处于另一个写操作过程中;
如果上述两个判断结果均为否,则对该数据进行写操作的过程中,以所述全局写锁进行加写锁、解写锁的操作。
一种写锁操作***,包括数据单元,第一判断单元,第二判断单元,加、解写锁单元,其中,
数据单元,用于存储数据;
第一判断单元,用于在对数据执行写操作前,判断所有计算核心中是否存在对该数据的读操作过程;
第二判断单元,用于在对数据执行写操作前,判断所述数据是否处于另一个写操作过程中;
加、解写锁单元,用于在第一判断单元和第二判断单元的判断结果皆为否的情况下,在对所述数据进行写操作的过程中,以所述全局写锁进行加写锁、解写锁的操作。
由以上本申请实施例提供的技术方案可见,本申请实施例使得不同核心的线程对同一数据进行读操作时,独立的对该核心对应的私有引用计数操作。这些不同核心对应的私有引用计数不需要在各个核之间同步,因此执行效率得到提升。而且,读锁的扩展性也得以提高,即无论多少个核心的线程并发加、解读锁,加解读锁的时间几乎都不会增加,从而提高了执行效率。
附图说明
为了更清楚地说明本申请实施例或现有技术中的技术方案,下面将对实施例或现有技术描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图仅仅是本申请中记载的一些实施例,对于本领域普通技术人员来讲,在不付出创造性劳动性的前提下,还可以根据这些附图获得其他的附图。
图1为本申请读锁操作方法一个实施例的流程图;
图2为本申请读锁操作***一个实施例的模块图;
图3为本申请写锁操作方法一个实施例的流程图;
图4为本申请写锁操作***一个实施例的模块图。
具体实施方式
本申请实施例提供一种读锁操作方法、写锁操作方法及***。
为了使本技术领域的人员更好地理解本申请中的技术方案,下面将结合本申请实施例中的附图,对本申请实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本申请一部分实施例,而不是全部的实施例。基于本申请中的实施例,本领域普通技术人员在没有作出创造性劳动前提下所获得的所有其他实施例,都应当属于本申请保护的范围。
首先介绍本申请读锁操作方法的一个实施例。
图1示出了本申请读锁操作方法一个实施例的流程图。如图1所示,该实施例的方法包括:
S100:设置每个核心对应的私有引用计数。
现代CPU采用了大量的技术来抵消内存访问带来的延迟。读写内存数据期间,CPU可以执行成百上千条指令。多级静态随机存储器(Static Random Access Memory,SRAM)缓存(以下简称为缓存)是减小这种延迟带来的影响的主要手段。
例如,对于双核心的计算机***,核心1和核心2分别有对应的缓存1和缓存2。所述缓存可以是计算核心的缓存。例如,对于CPU而言,CPU内部常常带有一级缓存、二级缓存,甚至有的CPU还带有三级缓存。对于包括一级缓存、二级缓存的CPU来说,CPU需要操作的数据,一种情况是从内存中先读入到二级缓存,再从二级缓存读入到一级缓存,再从一级缓存读入到CPU中执行。一般来说,越靠近CPU的存储器,速度越快,但造价也越高;越远离CPU的存储器,速度越慢,但造价也越便宜。一般在靠近CPU的存储器中存储CPU读写频繁的数据,以提高造价高的存储器的利用率。
本步骤中,优选的,可以将私有引用计数(private_read_ref)置于缓存中。例如,可以将私有引用计数设置在CPU的一级缓存中。当然,视CPU的体系架构以及不同级的存储器的容量,也可以将私有引用计数设置在二级缓存中,还可以设置在其它读取速度与CPU原子操作速度同量级的存储器中。本申请实施例对此并不做特别限定。事实上,缓存往往对程序是透明的,也就是说程序没办法控制一个变量是否要放到缓存中,以及要放在哪一级缓存。当程序要操作一个变量时,CPU可以检查该变量是否在一级缓存中,如果有,直接从一级缓冲中读;如果没有,可以检查是否在二级缓存中:如果有,把变量从二级缓存载入到一级缓存,如果二级缓存也没有,把变量从内存中载入到二级缓存和一级缓存中。
现有技术中,不同线程对同一数据的读操作,涉及同一引用计数,即对同一引用计数操作。这个引用计数read_ref,按照计算机领域一般规则称为全局read_ref。特别是同一计算核心的不同线程,包括同一进程中的不同线程,或者是不同进程中的不同线程,在对同一数据进行读操作时,对同一个全局read_ref进行自加(++)或自减(--)操作。如果在多核心计算机***中,对于多个核心的情况,仍然只采用一个全局引用计数,则会出现如背景技术中分析得到的问题。
本步骤中,对于不同核心,为每个核心设置私有应用计数。例如,对于核心1,设置其对应的私有引用计数,例如为read_ref_core1;对于核心2,也设置其对应的私有引用计数,例如为read_ref_core2。对于还包括其它核心的情况,可以依此类推。
对于每一核心对应的私有引用计数,可以不是永久(或称之为固定)分配的,而是临时分配的。例如,可以在每个核心的线程对数据进行首次加读锁前进行分配;在该核心的线程对该数据的读操作执行完毕之后收回该私有引用计数。具体的,可以设置一个私有引用计数的数组[read_ref]。在每个核心的线程对数据进行首次加读锁前,申请分配该[read_ref]数组中的一项。该数组[read_ref]的空间可以设置的足够大。数组中每一项可以设置为整形(int)。数组中每一项的初始值可以初始化为0。当然,针对某一数据的读操作,也可以将[read_ref]数组中的每一项固定分配至每一个核心。
优选地,实际操作中,可以将[read_ref]数组中的每一项分配到缓存中的一个缓存行中(cache line)。缓存行是多核心CPU维护缓存一致性的最小单元,也是内存交换的实际单位。实际当中,大多数平台上的一个缓存行要大于8字节,大多缓存行是64字节。如果[read_ref]数组定义为int型,则为8字节。可见,一个缓存行可以存储8个read_ref。如果一个缓存行中存储不止一个read_ref的情况,则操作数组中不同元素时就会有冲突。为了避免冲突,可以将[read_ref]数组中每一个read_ref存储在一个缓存行中。例如,可以将[read_ref]数组中的每一项声明为一个结构体,同时声明一个结构体大小为64字节。这样,[read_ref]数组中的每一项都独占一个缓存行,可以避免操作时产生冲突。
S110:在不同核心的线程对同一数据进行读取过程中,以不同核心对应的私有引用计数进行加读锁、解读锁操作。
例如,同一计算机***中包括2个计算核心,分别为核心1和核心2。再例如,核心1和核心2都要对同一数据进行读取。按照S100,核心1可以申请1个私有引用计数,标记为read_ref_core1;类似的,核心2也可以各申请1个私有引用计数,如为read_ref_core2。
这样,在核心1的线程对该数据进行读取过程中,首先进行加读锁。即,核心1的私有引用计数read_ref_core1执行加1操作。这样,read_ref_core1由初始值0变为1。之后,核心1的线程对该数据进行读取。读操作执行完毕之后,进行解读锁操作。即,核心1的私有引用计数read_ref_core1执行减1操作。这样,read_ref_core1由1变为0。
类似的,在核心2的线程对该数据进行读取过程中,首先进行加读锁。即,核心2的私有引用计数read_ref_core2执行加1操作。这样,read_ref_core2由初始值0变为1。之后,核心2的线程对该数据进行读取。读操作执行完毕之后,进行解读锁操作。即,核心2的私有引用计数read_ref_core2执行减1操作。这样,read_ref_core2由1变为0。
本申请实施例采用的上述方式,使得不同核心的线程对同一数据进行读操作时,独立的对该核心对应的私有引用计数操作。这些不同核心对应的私有引用计数不需要在各个核之间同步,因此执行效率得到提升。而且,读锁的扩展性也得以提高,即无论多少个核心的线程并发加、解读锁,加解读锁的时间几乎都不会增加。
此外,不同核心对应的私有引用计数不需要在各个核之间同步,省去了各个核心之间的通信过程,从而省去了核间通信所需的带宽、时间等的开销。
所述S110具体可以包括如下步骤:
S111:第一核心的线程对数据进行读取过程中,以第一核心对应的私有引用计数进行加读锁、解读锁操作。
S112:第二核心的线程对所述数据进行读取过程中,以第二核心对应的私有引用计数进行加读锁、解读锁操作。
上述以私有引用计数进行的加读锁操作,具体包括:不同核心的进程以各核心对应的私有引用计数执行加1操作。以私有引用计数进行的解读锁操作,具体包括:不同核心的进程以各核心对应的私有引用计数执行减1操作。在加读锁和解读锁操作之间,各核心的进程可以对所述数据进行读取。
需要说明的是,同一核心的多个不同线程对同一数据进行读操作的过程,可以以同一私有计数器执行加读锁、解读锁操作。例如,在核心1的线程1对该数据进行读取过程中,首先进行加读锁。核心1的私有引用计数read_ref_core1执行加1操作。这样,read_ref_core1由初始值0变为1。之后,核心1的线程1对该数据进行读取。在核心1的线程1对该数据进行读取的过程中,核心1的线程2也要对同一数据执行读操作,则将read_ref_core1的值加1,并对该数据进行读取。则此时read_ref的值为2。核心1的线程1的读操作执行完毕之后,将read_ref_core1的值减1,解读锁。此时,read_ref_core1的值变为1。之后,核心1的线程2对该数据的读操作执行完毕,将read_ref_core1的值减1,解读锁。此时,read_ref_core1的值变为0。这样,对于同一核心来说,无论其多少个线程并发加、解读锁,加解读锁的时间几乎都不会增加。
还需要说明的是,为了避免数据的不一致情况发生,本申请实施例中的读锁,仍然与写锁是互斥的。例如,具有多个核心的计算机***中,设置一个全局写锁,如为global_writer_id。某一线程对一数据要执行写操作,则在执行写操作之前,对该数据加写锁。例如,某一核心的线程1将global_writer_id的值更新为线程1的ID(例如global_writer_id的数据类型为整形,且初始值为0;任一线程的ID不为0),之后对该数据进行写操作。在写的过程中,某一核心(与前一加写锁的线程可以是同核心或不同核心)的线程,这里称为线程2,要对同一数据执行读操作,申请到对应该核心的私有引用计数。该私有引用计数例如初始化为0。但是,因global_writer_id的值此时不为0,因此线程2不能加读锁,也不能对该数据进行读操作。线程1的写操作执行完毕之后,解写锁,即将global_writer_id的值更新为0。线程2在前次加读锁失败并等待一段时间后,得知global_writer_id的值此时为0,可以加读锁。线程2也可能在前次加读锁失败后,每隔一定时间尝试进行加读锁;当global_writer_id的值为0时,重试加读锁成功。这样,线程2将其申请到的私有引用计数的值加1,之后对该数据进行读操作。此时线程2的私有引用计数的值为1。线程2的读操作执行完毕之后,解读锁,即将其对应的私有引用计数的值进行减1操作,变为0。
基于此,S110中不同核心的线程对对应的私有引用计数进行加读锁操作前,还可以包括:
S101:不同核心的线程检查所述数据是否处于写操作过程,在检查结果为否时触发执行S110。
是否处于写操作过程,可以通过检查全局写锁的状态实现。例如,可以检查全局写锁是否为0,并且检查结果为0时执行S110。
反之,如果检查全局写锁的值不为0,则意味着当前对该数据存在写操作。则基于前述写锁和读锁的互斥行,此时不能对该数据加读锁,也就不能对该数据进行读操作。该情况下,需要等待全局写锁变为0之后,再执行S110。
该S101可以是在S100之后执行,也可以是在S100之前执行。
需要说明的是,对于加读锁,为了进一步避免在检查global_writer_id值的操作与相应的读操作之间有另一线程同时为写操作而进行的加写锁操作,即为了避免该种情况下导致的冲突检测失效,将该核心对应的私有引用计数的值加1之后,还将再次检查此时的该global_writer_id值是否为0。如果不为0,才执行读操作。
以下介绍本申请读锁操作***的一个实施例。图2示出了该***实施例的模块图。
如图2所示,本申请一实施例中的读锁操作***包括第一计算核心11a,第二计算核心11b,第一缓存单元12a,第二缓存单元12b,数据单元13,每个所述计算核心对应一个唯一的缓存单元。
其中:
数据单元13,用于存储数据;
第一缓存单元12a,用于保存为第一计算核心的分配的第一私有引用计数;
第二缓存单元12b,用于保存为第二计算核心的分配的第二私有引用计数;
第一计算核心11a和第二计算核心11b,用于对所述数据单元中的同一数据进行读取;且,
第一计算核心11a的线程对所述数据进行读取过程中,以第一核心对应的私有引用计数进行加读锁、解读锁操作;
第二计算核心11b的线程对所述数据进行读取过程中,以第二核心对应的私有引用计数进行加读锁、解读锁操作。
其中:
所述第一缓存单元12a可以是第一计算核心的缓存;
所述第二缓存单元12b可以是第二计算核心的缓存。
前面在方法实施例中提到,设置每个核心对应的私有引用计数,可以在每个核心的线程对数据进行首次加读锁前分配该核心对应的私有引用计数,或也可以固定分配每一核心的私用引用计数。例如,可以设置一个私有引用计数的数组[read_ref]。在每个核心的线程对数据进行首次加读锁前,申请分配该[read_ref]数组中的一项。该数组[read_ref]的空间可以设置的足够大。数组中每一项可以设置为整形(int)。数组中每一项的初始值可以初始化为0。当然,针对某一数据的读操作,也可以将[read_ref]数组中的每一项固定分配至每一个核心。优选地,实际操作中,可以将[read_ref]数组中的每一项分配到缓存中的一个缓存行中(cacheline)。缓存行是多核心CPU维护缓存一致性的最小单元,也是内存交换的实际单位。实际当中,大多数平台上的一个缓存行要大于8字节,大多缓存行是64字节。如果[read_ref]数组定义为int型,则为8字节。可见,一个缓存行可以存储8个read_ref。如果一个缓存行中存储不止一个read_ref的情况,则操作数组中不同元素时就会有冲突。为了避免冲突,可以将[read_ref]数组中每一个read_ref存储在一个缓存行中。例如,可以将[read_ref]数组中的每一项声明为一个结构体,同时声明一个结构体大小为64字节。这样,[read_ref]数组中的每一项都独占一个缓存行,可以避免操作时产生冲突。
结合上述内容,本申请读锁操作***的一个实施例中,不同核心的缓存可以对应不同的缓存行中。例如,所述第一缓存单元对应第一缓存行,所述第二缓存单元对应第二缓存行。
所述读锁操作***实施例中,还可以包括检查单元14,用于检查所述数据是否处于写操作过程,并在否的情况下触发各计算核心对相应私有引用计数进行的加读锁、解读锁操作。
上述对私有引用计数进行的加读锁操作,具体包括:各核心的进程对该核心对应的私有引用计数执行加1操作。对私有引用计数进行的解读锁操作,具体包括:各核心的进程对该核心对应的私有引用计数执行减1操作。在加读锁和解读锁操作之间,各核心的进程可以对所述数据进行读取。
以下介绍本申请写锁操作方法的一个实施例。图3示出了该方法实施例的流程图。如图3所示,本申请的写锁操作方法实施例包括:
S300:对数据执行写操作前,判断所有计算核心中是否存在对该数据的读操作过程。
所述判断所有计算核心是否存在对该数据的读过程,具体的,可以通过便利各计算核心的对应该数据的私有引用计数是否为0实现。如果为0,说明该数据处于读操作过程;如果不为0,则说明该数据不处于读操作过程。
S310:对数据执行写操作前,判断所述数据是否处于另一个写操作过程中。
S310,具体的可以通过判断针对该数据的全局写锁是否为0实现。如果为0,说明该数据并没有处于另一个写操作过程中;如果不为0,说明有另一个写操作过程存在。
S320:如果上述S310、S320的判断结果均为否,则对该数据进行写操作的过程中,以所述全局写锁进行加写锁、解写锁的操作。
具体的,在进行写操作之前,对该数据加写锁;在写操作之后,对该数据解写锁。
S320中的全局变量例如为global_writer_id。加写锁,可以是将该global_writer_id的值更新为写线程的ID;解写锁,可以是将该global_writer_id的值更新为0。
类似的,对于加写锁,为了进一步避免在检查各核心私有引用计数值的操作与相应的写操作之间有另一线程同时为读操作而进行的加读锁操作,即为了避免该种情况下导致的冲突检测失效,将global_writer_id的值更新为写线程的ID之后,还将再次检查此时各核心私有引用计数值是否为0。如果不为0,才执行写操作。
上述写锁操作方法可以基于前述读锁操作方法或读锁操作***。
以下介绍本申请写锁操作***的一个实施例。图4示出了该***实施例的模块图。如图4所示,本申请的写锁操作***实施例包括:
数据单元3,用于存储数据;
第一判断单元21a,用于在对数据执行写操作前,判断所有计算核心中是否存在对该数据的读操作过程;
第二判断单元21b,用于在对数据执行写操作前,判断所述数据是否处于另一个写操作过程中;
加、解写锁单元22,用于在第一判断单元和第二判断单元的判断结果皆为否的情况下,在对所述数据进行写操作的过程中,以所述全局写锁进行加写锁、解写锁的操作。
具体的,在进行写操作之前,对该数据加写锁;在写操作之后,对该数据解写锁。
全局变量例如为global_writer_id。加写锁,可以是将该global_writer_id的值更新为写线程的ID;解写锁,可以是将该global_writer_id的值更新为0。
上述写锁操作***可以基于前述读锁操作方法或读锁操作***。
上述实施例阐明的***、装置、模块或单元,具体可以由计算机芯片或实体实现,或者由具有某种功能的产品来实现。
为了描述的方便,描述以上装置时以功能分为各种单元分别描述。当然,在实施本申请时可以把各单元的功能在同一个或多个软件和/或硬件中实现。
通过以上的实施方式的描述可知,本领域的技术人员可以清楚地了解到本申请可借助软件加必需的通用硬件平台的方式来实现。基于这样的理解,本申请的技术方案本质上或者说对现有技术做出贡献的部分可以以软件产品的形式体现出来,该计算机软件产品可以存储在存储介质中,如ROM/RAM、磁碟、光盘等,包括若干指令用以使得一台计算机设备(可以是个人计算机,服务器,或者网络设备等)执行本申请各个实施例或者实施例的某些部分所述的方法。
本说明书中的各个实施例均采用递进的方式描述,各个实施例之间相同相似的部分互相参见即可,每个实施例重点说明的都是与其他实施例的不同之处。尤其,对于***实施例而言,由于其基本相似于方法实施例,所以描述的比较简单,相关之处参见方法实施例的部分说明即可。
本申请可用于众多通用或专用的计算机***环境或配置中。例如:个人计算机、服务器计算机、手持设备或便携式设备、平板型设备、多处理器***、基于微处理器的***、置顶盒、可编程的消费电子设备、网络PC、小型计算机、大型计算机、包括以上任何***或设备的分布式计算环境等等。
本申请可以在由计算机执行的计算机可执行指令的一般上下文中描述,例如程序模块。一般地,程序模块包括执行特定任务或实现特定抽象数据类型的例程、程序、对象、组件、数据结构等等。也可以在分布式计算环境中实践本申请,在这些分布式计算环境中,由通过通信网络而被连接的远程处理设备来执行任务。在分布式计算环境中,程序模块可以位于包括存储设备在内的本地和远程计算机存储介质中。
虽然通过实施例描绘了本申请,本领域普通技术人员知道,本申请有许多变形和变化而不脱离本申请的精神,希望所附的权利要求包括这些变形和变化而不脱离本申请的精神。