目的:
我们想去创建一个工作者线程池来处理客户端的连接,让每个线程去处理各自的客户,或者完成其他类型的工作。
方法:
在concurrent.futures库中包含了ThreadPoolExecutor类可以实现这个目的。下面的例子是一个简单TCP服务器,使用线程池来处理客户端:
from socket import AF_INET, SOCK_STREAM, socket
from concurrent.futures import ThreadPoolExecutor
def echo_client(sock, client_addr):
print('Got connection from :', client_addr)
while True:
msg = sock.recv(65536)
if not msg:
break
sock.sendall(msg)
print(sock,'has been sent all message ...')
sock.close()
def echo_server(addr):
pool = ThreadPoolExecutor(128)
sock = socket(AF_INET, SOCK_STREAM)
sock.bind(addr)
sock.listen()
while True:
client_sock, client_addr = sock.accept()#服务器接收客户端请求
pool.submit(echo_client, client_sock, client_addr)# 提交给线程池中的处理函数去处理客户端
echo_server(('localhost',21000))# 服务器启动
同样,可以抛开concurrent.futures中的ThreadPoolExecutor,直接手动创建线程池,如果借助Queue则会变得容易:
from socket import SOCK_STREAM, AF_INET,socket
from threading import Thread
from queue import Queue
def echo_client(q):
sock, client_addr = q.get()
print('got connection:', client_addr)
while True:
msg = sock.recv(65536)
if not msg:
break
sock.sendall(msg)
print('client close')
sock.close()
def echo_server(addr, client_count):
q = Queue()
for x in range(client_count):
t = Thread(target=echo_client,args=(q,),daemon=True)
t.start()
sock = socket(SOCK_STREAM, AF_INET)
sock.bind(addr)
sock.listen(5)
while True:
sock, client_addr = sock.accept()
q.put(sock,client_addr)
当然又有一句很蹩脚的话,不建议各位这样使用!!应该去使用concurrent.futures中的ThreadPoolExecutor,这么做的优势在于提交任务者可以很好地拿到处理后的结果:
from concurrent.futures import ThreadPoolExecutor
import urllib.request
def request_url(url):
u = urllib.request.urlopen(url)
data = u.read()
return data
pool = ThreadPoolExecutor(10)
a = pool.submit(request_url,r'http://www.xxxxxx.com')
b = pool.submit(request_url,r'http://www.xxxxxx.org')
res1 = a.result()# 会阻塞等待上述中a取出结果后再解除阻塞
res2 = b.result()
讨论一个大家都很容易想到的问题,有些人认为,应该在服务器接收到一个客户端连接时就去开辟一个线程来处理这个客户端的事务,本质上来讲这样确实没有问题,但是,万一一个黑客用大量的客户端去访问服务时,有可能会因为开辟太多线程导致服务器挂掉,这是初学者都会想到的思路,但是,一旦涉及多并发程序,切记一定要限制线程个数,为了系统稳定和安全。下面为各位展示初学者的代码;
from threading import Thread
from socket import socket, AF_INET, SOCK_STREAM
def echo_client(sock, client_addr):
print('GOT a connection:', client_addr)
while True:
msg = sock.recv(65536)
if not msg:
break
sock.sendall(msg)
sock.close()
def echo_server(addr:tuple):
sock = socket(AF_INET, SOCK_STREAM)
sock.bind(addr)
sock.listen(5)
while True:
client_sock, client_addr = sock.accept()
t = Thread(target=echo_client,args=(client_sock, client_addr))
t.daemon = True
t.start()
echo_server(('localhost',21000))
这里也要注意,线程只适合做IO密集型的任务。综上关于线程池的内容就介绍到这里,如果想要了解更多请微信关注公众号: