1 引言
最近在了解Python原生的协程机制,通过生成器来实现协程,我最终想要实现几个功能:
第一:单进程、单线程的多任务处理
第二:实现一些基本的系统调用
第三:单进程、单线程实现一个多任务处理功能的web服务器
2 实现多任务
多任务可以通过多进程和多线程的方式很容易实现,但这个不是本篇文章的重点。本文章使用生成器实现多任务。
Task类比较简单,初始化方法指定一个唯一ID,run方法用于激活生成器函数,让生成器函数指定到第一个yield位置返回。
任务必须要被调度才能够运行,所以第二个类要封装一个调度器-scheduler类,调度器要具备如下基本功能:
- 存储所有的任务:使用字典存储所有的任务列表
- 存储就绪的任务:使用队列存储就绪的任务
- 创建新任务:新建Task对象
- 结束任务:将Task对象从任务字典中删除
- 调度任务:将Task对象加入ready队列中
- 定义一个循环,循环做的事就是不断从任务队列取任务,执行任务
class Task:
tid = 0
def __init__(self, target):
Task.tid += 1
self.tid = Task.tid # 定义唯一的id
self.target = target
self.send_val = None
def run(self):
return self.target.send(self.send_val)
class Scheduler: # 调度者类
def __init__(self):
self.task_storage = {}
self.ready = queue.Queue()
def new(self, target): # 创建新任务
task = Task(target)
self.task_storage[task.tid] = task
self.ready.put(task)
return task.tid
def exit(self, tid): # 退出任务
del self.task_storage[tid]
def schedule(self, task): # 将就绪任务存储到列表中
self.ready.put(task)
def main_loop(self): # 循环队列
while True:
task = self.ready.get()
try:
result = task.run()
except StopIteration:
self.exit(task.tid)
continue
self.schedule(task)
def sing():
for i in range(5):
yield
print("i am singing...")
def dance():
for i in range(5):
yield
print("i am dancing...")
if __name__ == '__main__':
s = Scheduler()
s.new(sing())
s.new(dance())
s.main_loop()
"""
i am singing...
i am dancing...
i am singing...
i am dancing...
i am singing...
i am dancing...
i am singing...
i am dancing...
i am singing...
i am dancing...
。。。
"""
main_loop代码是很重要的,这里着重分析一下:
s1:从队列中获取任务
s2:task.run()执行后,cpu就会运行task里面的函数,直到遇到第一个yield,将cpu使用权还给调度器
s3:将任务放回队列的队尾,等待下一次执行
现在我们通过生成器实现了单进程、单线程的多任务
3 系统调用基类
首先定义一个所有系统调用方法的基类,就叫做SystemCall,这个系统调用基类需要定义两个实例属性,分别记录当前task和当前task所在的调度器。
并且定义一个handler实例方法,用于实现系统调用的功能。
class SystemCall: # 所有系统调用类的抽象基类
def __init__(self):
self.task = None # 当前任务
self.schedule = None # 当前调度器
def handler(self):
raise NotImplementedError
然后重新定义main_loop方法
class Scheduler:
def main_loop(self): # 循环队列
while True:
task = self.ready.get()
try:
result = task.run() # 这里result保存着task函数里yield后面的值
if isinstance(result, SystemCall): # 如果task是SystemCall类的实例
result.task = task # 将当前任务赋给SystemCall实例
result.schedule = self # 将当前调度器赋给SystemCall实例
result.handler() # 执行相关操作
except StopIteration:
self.exit(task.tid)
continue
self.schedule(task)
3.1 获取task id
获取任务的进程ID是操作系统中最常用的功能。
class GetID(SystemCall):
def __init__(self):
super().__init__()
def handler(self):
self.task.send_val = self.task.tid # 将当前任务ID通过生成器对象的send方法给到生成器
self.schedule.ready.put(self.task) # 然后将task再次排到队列尾部
def sing():
for i in range(5):
tid = yield GetID()
print(f"task id:{tid}, i am singing...")
def dance():
for i in range(5):
tid = yield GetID()
print(f"task id:{tid}, i am dancing...")
if __name__ == '__main__':
s = Scheduler()
s.new(sing())
s.new(dance())
s.main_loop()
"""
task id:1, i am singing...
task id:2, i am dancing...
task id:1, i am singing...
task id:2, i am dancing...
task id:1, i am singing...
task id:2, i am dancing...
task id:1, i am singing...
task id:2, i am dancing...
task id:1, i am singing...
task id:2, i am dancing...
"""
3.2 创建新任务
类似windows的任务管理器,操作系统都有一个创建新任务的功能。
class New(SystemCall):
def __init__(self, target):
super().__init__()
self.target = target
def handler(self):
tid = self.schedule.new(self.target) # 通过调度器创建一个新任务
self.task.send_val = tid # 将任务的id发送到生成器中
self.schedule.schedule(self.task) # 使用调度器调度任务
def foo():
for i in range(5):
tid = yield GetID()
print(f"task id:{tid},i am foo function")
def bar():
for i in range(5):
tid = yield GetID()
print(f"task id:{tid}, i am bar function")
new_tid = yield New(foo())
if new_tid:
print(f"create new task success! tid is {new_tid}")
if __name__ == '__main__':
s = Scheduler()
s.new(bar())
s.main_loop()
"""
task id:1, i am bar function
task id:1, i am bar function
task id:1, i am bar function
task id:1, i am bar function
task id:1, i am bar function
create new task success! tid is 2
task id:2,i am foo function
task id:2,i am foo function
task id:2,i am foo function
task id:2,i am foo function
task id:2,i am foo function
"""
3.3 kill 任务
杀进程是操作系统必备功能之一
class Kill(SystemCall):
def __init__(self, tid):
super().__init__()
self.tid = tid
def handler(self):
# 从调度器的任务字典中获取到任务
task = self.schedule.task_storage.get(self.tid)
if task: # 如果字典中有这个task
task.target.close() # 关闭协程,直接抛出StopIteration异常
self.task.send_val = True # 给生成器一个True
self.schedule.schedule(self.task) # 再次调度任务
def foo():
for i in range(5):
tid = yield GetID()
print(f"task id:{tid},i am foo function")
def bar():
for i in range(5):
tid = yield GetID()
print(f"task id:{tid}, i am bar function")
new_tid = yield New(foo())
if new_tid:
print(f"create new task success! tid is {new_tid}")
yield
result = yield Kill(new_tid)
if result:
print(f"kill task success!")
if __name__ == '__main__':
s = Scheduler()
s.new(bar())
s.main_loop()
"""
task id:1, i am bar function
task id:1, i am bar function
task id:1, i am bar function
task id:1, i am bar function
task id:1, i am bar function
create new task success! tid is 2
task id:2,i am foo function
kill task success!
"""
4 协程版web服务器
def handle_client(client, addr):
print("Connection from", addr)
while True:
data = client.recv(65536)
if not data:
break
client.send(data)
client.close()
print("Client closed")
yield # yield 关键字将一个普通函数变成一个生成器函数
def server(port):
print("Server starting")
sock = socket.socket()
sock.bind(("", port))
sock.listen(5)
while True:
# 阻塞
client, addr = sock.accept()
yield New(handle_client(client, addr))
def alive():
while True:
print("I'm alive!")
yield
if __name__ == '__main__':
s = Scheduler()
s.new(alive())
s.new(server(8888))
s.main_loop()