导入
简介
本课程为接下来的实践练习奠定了基础,我们将通过该练习研究在使用不同并发模型设计和实现Web服务时,GIL所带来的影响。
简介
我们已经讨论过全局解释器锁(GIL)以及它在编写 Python 并发程序时带来的挑战。根据具体使用场景,我们可以选择多线程或异步的方式,但无论采用哪种方式,都必须注意 GIL 对解决方案可能造成的影响。在本节中,我们将通过一个 Web 服务来演示,先用线程实现,再用 asyncio 实现,并分析在处理计算密集型任务时会遇到的问题。本节中的示例灵感来自 Dave Beazley 的 PyCon 演讲。
环境设置
我们将创建一个非常简单的 Web 服务,该服务根据客户端请求返回第 n 个质数。例如,如果输入为 1,则服务返回第一个质数 2;如果输入为 10,则返回第 10 个质数 29。我们将使用一种效率极低的计算密集型算法来寻找第 n 个质数。你无需关注寻找质数的具体代码实现,因为它可以替换为任何计算密集型任务。
最初,我们将从单线程版本的服务器开始。启动服务的代码如下:
1 def run_service(self): 2 s = socket.socket() 3 s.bind(('', self.server_port)) 4 5 # 将套接字置于监听模式 6 s.listen(5) 7 client_socket, addr = s.accept() 8 data = client_socket.recv(4096).decode() 9 nth_prime = int(data) 10 print("请求查找第 {0} 个质数".format(nth_prime)) 11 12 prime = self.find_nth_prime(nth_prime) 13 client_socket.send(str(prime).encode())
完整代码见下方代码框。
1from threading import Thread 2from threading import Lock 3from concurrent.futures import ProcessPoolExecutor 4 5import socket, time, random 6 7 8class PrimeService(): 9 10 def __init__(self, server_port): 11 self.server_port = server_port 12 13 def find_nth_prime(self, nth_prime): 14 i = 2 15 nth = 0 16 17 while nth != nth_prime: 18 if self.is_prime(i) == True: 19 nth += 1 20 last_prime = i 21 22 i += 1 23 24 return last_prime 25 26 def is_prime(self, num): 27 if num == 2 or num == 3: 28 return True 29 30 div = 2 31 32 while div <= num / 2: 33 if num % div == 0: 34 return False 35 div += 1 36 return True 37 38 def run_service(self): 39 s = socket.socket() 40 s.bind(('', self.server_port)) 41 42 # 将套接字置于监听模式 43 s.listen(5) 44 client_socket, addr = s.accept() 45 data = client_socket.recv(4096).decode() 46 nth_prime = int(data) 47 print("请求查找第 {0} 个质数".format(nth_prime)) 48 49 prime = self.find_nth_prime(nth_prime) 50 client_socket.send(str(prime).encode()) 51 52 53def run_client(server_host, server_port): 54 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 55 server_socket.connect((server_host, server_port)) 56 server_socket.send("7".encode()) 57 result = server_socket.recv(4096).decode() 58 print("找到的质数为 {0}".format(result)) 59 60 61if __name__ == "__main__": 62 server_port = random.randint(10000, 65000) 63 server_host = "127.0.0.1" 64 server = PrimeService(server_port) 65 66 server_thread = Thread(target=server.run_service, daemon=True) 67 server_thread.start() 68 69 run_client(server_host, server_port) 70 71 time.sleep(10)