美文网首页python自学
自己实现 Eventloop

自己实现 Eventloop

作者: Lupino | 来源:发表于2019-01-21 10:50 被阅读28次

自己尝试实现一个事件中心 EventLoop

生成器(generator

如下使用关键字 yield 来产生一个生成器,在生成器中函数可以暂时离开原来的状态执行外部的逻辑。

def generator_function():
    count = 0
    while True:
        print('start yield')
        yield count
        print('end yield')
        count ++

for i in generator_function():
    print(i)

生成器是实现 Eventloop 最重要的部分,我们可以构建很多生成器,通过一个循环来协同执行生成器里面的逻辑,这样我们就可以实现简单的事件循环。

简单的 Eventloop

如下代码,我们用 while True 实现一个简单的 Eventloop

def func1():
      yield 
      print('logic1')
      yield 
      print('logic2')
      # ...
def func2():
      yield 
      print('logic3')
      yield 
      print('logic4')
      # ...

coro1 = func1()
coro2 = func2()
coros = [coro1, coro2]
while True:
    if len(coros) == 0:
        break
    coro = coros.pop()

    try:
        next(coro)
        coros.insert(0, coro)
    except StopIteration:
        pass

我们可以用 yield from 来将生成器提交到上层。

yield fromEventloop

def func1():
      yield 
      print('logic1')
      yield 
      print('logic2')
      # ...
def func2():
      yield 
      print('logic3')
      yield 
      print('logic4')
      # ...
def func3():
    print('logic5')
    yield from func1()
    print('logic6')
    yield from func2()
    # ...

coro1 = func1()
coro2 = func2()
coro3 = func3()
coros = [coro1, coro2, coro3]
while True:
    if len(coros) == 0:
        break
    coro = coros.pop()

    try:
        next(coro)
        coros.insert(0, coro)
    except StopIteration:
        pass

有了 yield from 我们可以实现复杂点的 Eventloop

使用 asyncawait

def func1():
      yield 
      print('logic1')
      yield 
      print('logic2')
      # ...
def func2():
      yield 
      print('logic3')
      yield 
      print('logic4')
      # ...
async def func3():
    print('logic5')
    await func1()
    print('logic6')
    await func2()
    # ...

coro1 = func1()
coro2 = func2()
coro3 = func3()
coros = [coro1, coro2, coro3]
while True:
    if len(coros) == 0:
        break
    coro = coros.pop()

    try:
        next(coro)
        coros.insert(0, coro)
    except StopIteration:
        pass

micropythonasync/awaityield from 的作用相同

真正的 Eventloop

根据上面的分析,我们实现自己的 Eventloop 如下:

import time

def ticks_ms():
   try:
       return time.ticks_ms()
   except:
       return int(time.time() * 1000)

class Eventloop(object):
   def __init__(self):
       self.runq = []
       self.waitq = []
       self.cur_task = None

   def call_soon(self, cb):
       self.runq.insert(0, cb)

   def call_later(self, cb, delay):
       self.call_at(cb, delay + ticks_ms())

   def call_at(self, cb, t):
       self.waitq.insert(0, {'cb': cb, 'call_at': t})

   def wait(self, delay=1):
       time.sleep(delay / 1000)

   def run_forever(self):
       while True:
           self.cur_task = None
           if len(self.runq) == 0:
               if len(self.waitq) == 0:
                   self.wait()
               else:
                   min = self.waitq[0]
                   for wait in self.waitq:
                       if min['call_at'] > wait['call_at']:
                           min = wait

                   if min['call_at'] <= ticks_ms():
                       self.waitq.remove(min)
                       self.call_soon(min['cb'])
                       continue
                   else:
                       self.wait(min['call_at'] - ticks_ms())
           else:
               delay = 0
               cb = self.runq.pop()
               self.cur_task = cb
               v = False
               try:
                   v = next(cb)
               except StopIteration:
                   continue

               if v is False:
                   continue
               elif isinstance(v, int):
                   delay = v

               if delay > 0:
                   self.call_later(cb, delay)
               else:
                   self.call_soon(cb)


def sleep(t):
   yield t * 1000

我们写一个简短的代码测试一下:

# for micropython
from eventloop import Eventloop, sleep

async def func0():
    while True:
        print('func0')
        await sleep(1)

def func1():
    while True:
        print('func1')
        yield from sleep(5)

def func2():
    while True:
        print('func2')
        for y in sleep(3):
            yield y

loop = Eventloop()

loop.call_soon(func0())
loop.call_soon(func1())
loop.call_soon(func2())

loop.run_forever()
# for python3
from eventloop import Eventloop, sleep

def func0():
    while True:
        print('func0')
        for y in sleep(1):
            yield y

def func1():
    while True:
        print('func1')
        yield from sleep(5)

def func2():
    while True:
        print('func2')
        yield from sleep(3)

loop = Eventloop()

loop.call_soon(func0())
loop.call_soon(func1())
loop.call_soon(func2())

loop.run_forever()
# for python2
from eventloop import Eventloop, sleep

def func0():
    while True:
        print('func0')
        for y in sleep(1):
            yield y

def func1():
    while True:
        print('func1')
        for y in sleep(5):
            yield y

def func2():
    while True:
        print('func2')
        for y in sleep(3):
            yield y

loop = Eventloop()

loop.call_soon(func0())
loop.call_soon(func1())
loop.call_soon(func2())

loop.run_forever()

结语

到这个我们已经实现了一个功能不健全的 Eventloop,我们通过不同的方式支持不同的 python 实现。

有兴趣的读者可以尝试的实现,Lock, Future 等好玩的东西,对于了解原理有莫大的帮助。

相关文章

网友评论

    本文标题:自己实现 Eventloop

    本文链接:https://www.haomeiwen.com/subject/pzrxjqtx.html