模拟使用线程池, 事件驱动IO模型, 这里使用的select
1. 采用对socket,初始化完成之后,内核状态是不可读,不可写(无东西可读)
2. 线程池完成工作之后, 回调_complete, 把线程计算结果及回调函数放入pending的list
3. 发送一个字节, 告诉内核select, 返回flieno那个socket可读,执行handle_receive把pending依次处理完成。
应用场景:
接收大量的fd,内存多大接多少
线程池处理结果
回调, fd清理收尾
#!/usr/bin/env python
class EventHandler(object):
"""An I/O event loop
"""
def fileno(self):
raise NotImplemented
def wants_to_receive(self):
"""Return True if receiving is allowed
"""
return False
def handle_receive(self):
"""Perform the receive operation
"""
pass
def wants_to_send(self):
"""Return True if sending is requested
"""
return False
def handle_send(self):
"""send data
"""
pass
from concurrent.futures import ThreadPoolExecutor
import socket
class ThreadPoolHandler(EventHandler):
def __init__(self, nworkers=3):
a = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
a.bind(('127.0.0.1', 0))
a.listen(1)
connect_address = a.getsockname()
self.writer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.writer.connect(connect_address)
self.reader, addr = a.accept()
a.close()
self.pending = []
self.pool = ThreadPoolExecutor(nworkers)
def fileno(self):
return self.reader.fileno()
def _complete(self, callback, r):
self.pending.append((callback, r.result()))
self.writer.send(b'x') # wake
def run(self, func, args={}, kwargs={}, *, callback):
r = self.pool.submit(func, *args, **kwargs)
r.add_done_callback(lambda r: self._complete(callback, r))
def wants_to_receive(self):
return True
def handle_receive(self):
"""Run callback functions of complete work
"""
for callback, result in self.pending:
callback(result)
self.reader.recv(1) # recv x
self.pending = []
class UDPServer(EventHandler):
"""An UDP io event loop handler
"""
def __init__(self, address):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.bind(address)
def fileno(self):
return self.sock.fileno()
def wants_to_receive(self):
# blocking
return True
import select
def event_loop(handlers):
while True:
wants_recv = [h for h in handlers if h.wants_to_receive()]
wants_send = [h for h in handlers if h.wants_to_send()]
can_recv, can_send, err = select.select(wants_recv, wants_send, [])
for h in can_recv:
h.handle_receive()
for h in can_send:
h.handle_send()
def fib(n):
if n < 2:
return 1
else:
return fib(n-1) + fib(n-2)
class UDPFibServer(UDPServer):
def handle_receive(self):
msg, addr = self.sock.recvfrom(1024)
n = int(msg)
pool.run(fib, (n,), callback=lambda r: self.respond(r, addr))
def respond(self, result, addr):
self.sock.sendto(str(result).encode("ascii"), addr)
if __name__ == "__main__":
pool = ThreadPoolHandler(16)
handlers = [pool, UDPFibServer(('', 7777))]
event_loop(handlers)