并发

以异步的方式发送您的请求。

异步的精髓在于并发。并发的核心思想是:在同步执行的基础下,先执行完当前的一个任务,在任务的某一个地方可以等待其自己完成时,去操作另一个任务;直到操作执行到该任务的等待部分,再转到下一个需要执行的任务。

简单来说,其实就是要把所有衣服塞进十台洗衣机,先塞满一台,按下开关,不等这台洗衣机洗完就去开另一台洗衣机,在搞完这台洗衣机之后再去搞其他洗衣机。大大提升了效率。

但是单单只有并发,最大的缺点就是:它是无序混乱的,而且无法灵活的变更数据。但是turingAPI的异步模型为您提供了一个解决办法——数据关联和数据互补

那么,我们下面先开始来学习最基础的并发。

分层并发

分层并发是turingAPI异步模型的一个非常重要的概念。其核心思想是:

封装一层,再封装一层,最后再封装一层。

是的,可能就是这么的离谱。但是这种“多层封装”的思想实际上是非常实用的。

turingAPI异步模型中最基础的单位是task,task可以将一个同步函数包装为一个可以进行异步调用的task单位。

举个例子,我们需要使用turingAPI刷浏览量,但是使用while循环+getWorkDetail这种方法实在太慢,这时我们就要用到异步。但是问题来了,getWorkDetail方法是一个同步的方法,它根本是无法被以异步形式调用的。可能有人会说到可以用asyncio.get_running_loop().run_in_executor(None,user.getWorkDetail,.......)这种方式来进行异步调用,但是这太麻烦。我们可以直接使用turingAPI提供的task类,将其变为一个包含了异步调用方法的对象,并且可以对其进行正常的调用。

温馨提醒:在查阅以下文档之前,如果您还不知道数据关联和数据互补的运行原理,您可以忽略注释带有“数据关联”“数据互补”的成员变量或函数

task

task类的所有变量成员如下所示:

func=None    #要调用的函数
name=None    #任务名称(很重要,在数据关联、数据互补时task进行互相辨认的唯一途径)
args=[]      #直接传参
kwargs={}    #关键字传参,参数名称(str):传参内容(any)
returnFunc=None   #在完成调用函数后将返回的值放入所属事件流时,使用的处理函数。传参格式为:func
#的返回值,task对象的self,所以returnFunc储存的函数必须只有两个必填参数
association=None  #需要进行关联的task名称
event=None   #所属事件流
associationFunc=None   #数据关联、数据互补拿到数据后进行对数据处理的函数

__init__方法:

这里不过多赘述,直接上代码。

def __init__(self,func,name=None,args=[],kwargs={},returnFunc=None,event=None,association=None,associationFunc=None):
    self.func=func
    self.name=name
    self.args=args
    self.kwargs=kwargs
    self.returnFunc=returnFunc
    self.event=event
    self.association=association
    self.associationFunc=associationFunc

run方法:

run方法用于对这个task内储存的func进行异步的调用。平时一般用不到,因为task是一个基本单位,只封装了一个函数,直接调用的话,没什么成效。我们一般用event的run函数来对所有task进行并发。

为了方便直接调用,我们把run方法设为同步形式。用它作为一个间接调用异步形式runTask的方法。也就是说它的代码只有这两行:

def run(self):
       asyncio.run(self.runTask())

如果您想以异步形式运行这个run,您可以使用另外的runTask方法。但是您不能直接调用它,您可以

await task.runTask()

import asyncio
asyncio.run(task.runTask())

event

event,顾名思义,也就是事件。我们称它的对象为事件流。一个事件由什么构成?答:由任务构成。所以,event是多个task的载体,它可以对多个task进行集中操作、集体并发和以自己为返回值载体为多个task建立数据关联。结合上文提到的turingAPI异步模型核心思想,我们可以把event看作第二层的封装。

event的所有成员变量如下:

tasks=[]     #事件流所承载的所有task序列
name=None    #事件流的名称,在进行数据互补时,这是多个event和task唯一辨认每一个event的方式
returns={}   #在并发task后所有拥有名称并正确指向该事件流的task的返回值。键为task名称,值为返回
#值(可能经过returnFunc的处理)
anonymousReturns=[]  #在并发task后所有没有名称且正确指向该事件流的task的返回值序列
association = {}   #在进行数据互补时,所有task的互补信息。格式为
#需要关联的taskName : [关联到的eventName1 , eventName2.....]
eventPool=None #所属事件池

__init__方法

也不多赘述了,上代码吧。

def __init__(self,tasks=[],name=None,association={},eventPool=None):
       self.tasks=tasks
       self.name=name
       self.association=association
       self.eventPool=eventPool

run方法

run方法可以对该事件流所承载的所有task进行并发。您可以使用同步的方式调用这个方法。

如果您想要以异步形式调用这个方法,您可以使用runEvent方法。但您不能直接调用它,您可以:

await event.runEvent()

import asyncio
asyncio.run(event.runEvent())

注意,returns和anonymousReturns成员在一次并发后不会被消除,而是一直保留着数据,直到下一次并发才会将数据清空。

除此之外,我们还在event外部为您提供了三个用于快捷添加、查找、删除task的方法,如下:

用于快速生成一个task,并对一个指定的事件流添加该task到tasks序列。

函数构造:

def addTask(event,func,name=None,args=[],kwargs={},returnFunc=None,association=None,associationFunc=None)

event:要将task添加到的事件流

func:生成的task的func成员

name:生成的task的name成员

args:生成的task的args成员

kwargs:生成的task的kwargs成员

returnFunc:生成的task的returnFunc成员

association:生成的task的association成员

associationFunc:生成的task的associationFunc成员

eventPool

eventPool,顾名思义,就是事件池。将多个事件流存在一个载体中,就形成了一个事件池。在下文我们称eventPool的对象为事件池。事件池可以对多个事件流进行有选择性的集体并发,并集中一处管理所承载的事件流。除此之外,它还可以作为一个中间载体,让多个event之间进行数据互补。这就是turingAPI异步模型核心思想中的第三层封装。

以下是eventPool的所有成员变量:

events=[]    #所承载的事件流序列
eventReturns={}  #在并发事件流后所有有名称并正确指向该事件池的事件流的返回值,格式为
#事件流名称 : [事件流returns成员,事件流anonymousReturns成员]
indexs={}    #每个拥有名称的事件流在events序列中的索引,格式为
#事件流名称 : 在events序列中的索引

run方法:

run方法可以对该事件池所承载的事件流进行有选择性的并发。您可以通过同步形式调用它。

函数构造:

def run(self,nameList=[],indexList=[])

nameList:需要进行并发的所有事件流名称

indexList:需要进行并发的所有事件流索引

两个参数可以叠加使用,两个都不填默认为并发所有。

如果您想要以异步形式run该事件池,您可以使用runEvents。它是异步的,参数设置与run相同。您可以使用以下方式调用它。

await eventPool.runEvents(xxxx,xxxx)
import asyncio
aysncio.run(eventPool.runEvents(xxxx,xxxx))

我们在eventPool类外还提供了三个用于增加、删除、查找事件流的顶级API

addEvent方法用于快速创建一个事件流并将其加入指定事件池的events序列。

函数构造:

def addEvent(eventPool,tasks=[],name=None,association={})

eventPool :要添加到的指定事件池

tasks : 事件流 tasks成员

name : 事件流名称

association : 事件流association成员

示例:

import turingAPI
user = turingAPI.icodeUser('your cookie')
ev = turingAPI.event(name='view')
for i in range(100):
    turingAPI.addTask(ev,user.getWorkDetail,'get{num}'.format(i),['a work id'],{})
ev.run()

Last updated