Python 如何创建一个线程池


Posted in Python onJuly 28, 2020

问题

你创建一个工作者线程池,用来响应客户端请求或执行其他的工作。

解决方案

concurrent.futures 函数库有一个 ThreadPoolExecutor 类可以被用来完成这个任务。 下面是一个简单的TCP服务器,使用了一个线程池来响应客户端:

from socket import AF_INET, SOCK_STREAM, socket
from concurrent.futures import ThreadPoolExecutor

def echo_client(sock, client_addr):
  '''
  Handle a client connection
  '''
  print('Got connection from', client_addr)
  while True:
    msg = sock.recv(65536)
    if not msg:
      break
    sock.sendall(msg)
  print('Client closed connection')
  sock.close()

def echo_server(addr):
  pool = ThreadPoolExecutor(128)
  sock = socket(AF_INET, SOCK_STREAM)
  sock.bind(addr)
  sock.listen(5)
  while True:
    client_sock, client_addr = sock.accept()
    pool.submit(echo_client, client_sock, client_addr)

echo_server(('',15000))

如果你想手动创建你自己的线程池, 通常可以使用一个Queue来轻松实现。下面是一个稍微不同但是手动实现的例子:

from socket import socket, AF_INET, SOCK_STREAM
from threading import Thread
from queue import Queue

def echo_client(q):
  '''
  Handle a client connection
  '''
  sock, client_addr = q.get()
  print('Got connection from', client_addr)
  while True:
    msg = sock.recv(65536)
    if not msg:
      break
    sock.sendall(msg)
  print('Client closed connection')

  sock.close()

def echo_server(addr, nworkers):
  # Launch the client workers
  q = Queue()
  for n in range(nworkers):
    t = Thread(target=echo_client, args=(q,))
    t.daemon = True
    t.start()

  # Run the server
  sock = socket(AF_INET, SOCK_STREAM)
  sock.bind(addr)
  sock.listen(5)
  while True:
    client_sock, client_addr = sock.accept()
    q.put((client_sock, client_addr))

echo_server(('',15000), 128)

使用 ThreadPoolExecutor 相对于手动实现的一个好处在于它使得 任务提交者更方便的从被调用函数中获取返回值。例如,你可能会像下面这样写:

from concurrent.futures import ThreadPoolExecutor
import urllib.request

def fetch_url(url):
  u = urllib.request.urlopen(url)
  data = u.read()
  return data

pool = ThreadPoolExecutor(10)
# Submit work to the pool
a = pool.submit(fetch_url, 'http://www.python.org')
b = pool.submit(fetch_url, 'http://www.pypy.org')

# Get the results back
x = a.result()
y = b.result()

例子中返回的handle对象会帮你处理所有的阻塞与协作,然后从工作线程中返回数据给你。 特别的,a.result() 操作会阻塞进程直到对应的函数执行完成并返回一个结果。

讨论

通常来讲,你应该避免编写线程数量可以无限制增长的程序。例如,看看下面这个服务器:

from threading import Thread
from socket import socket, AF_INET, SOCK_STREAM

def echo_client(sock, client_addr):
  '''
  Handle a client connection
  '''
  print('Got connection from', client_addr)
  while True:
    msg = sock.recv(65536)
    if not msg:
      break
    sock.sendall(msg)
  print('Client closed connection')
  sock.close()

def echo_server(addr, nworkers):
  # Run the server
  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(('',15000))

尽管这个也可以工作, 但是它不能抵御有人试图通过创建大量线程让你服务器资源枯竭而崩溃的攻击行为。 通过使用预先初始化的线程池,你可以设置同时运行线程的上限数量。

你可能会关心创建大量线程会有什么后果。 现代操作系统可以很轻松的创建几千个线程的线程池。 甚至,同时几千个线程等待工作并不会对其他代码产生性能影响。 当然了,如果所有线程同时被唤醒并立即在CPU上执行,那就不同了——特别是有了全局解释器锁GIL。 通常,你应该只在I/O处理相关代码中使用线程池。

创建大的线程池的一个可能需要关注的问题是内存的使用。 例如,如果你在OS X系统上面创建2000个线程,系统显示Python进程使用了超过9GB的虚拟内存。 不过,这个计算通常是有误差的。当创建一个线程时,操作系统会预留一个虚拟内存区域来 放置线程的执行栈(通常是8MB大小)。但是这个内存只有一小片段被实际映射到真实内存中。 因此,Python进程使用到的真实内存其实很小 (比如,对于2000个线程来讲,只使用到了70MB的真实内存,而不是9GB)。 如果你担心虚拟内存大小,可以使用 threading.stack_size() 函数来降低它。例如:

import threading
threading.stack_size(65536)

如果你加上这条语句并再次运行前面的创建2000个线程试验, 你会发现Python进程只使用到了大概210MB的虚拟内存,而真实内存使用量没有变。 注意线程栈大小必须至少为32768字节,通常是系统内存页大小(4096、8192等)的整数倍。

以上就是Python 如何创建一个线程池的详细内容,更多关于Python 创建线程池的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
利用python获得时间的实例说明
Mar 25 Python
python中 ? : 三元表达式的使用介绍
Oct 09 Python
python实现发送和获取手机短信验证码
Jan 15 Python
Python实现简单的获取图片爬虫功能示例
Jul 12 Python
flask使用session保存登录状态及拦截未登录请求代码
Jan 19 Python
python获取酷狗音乐top500的下载地址 MP3格式
Apr 17 Python
使用Anaconda3建立虚拟独立的python2.7环境方法
Jun 11 Python
对Python3 序列解包详解
Feb 16 Python
Python玩转加密的技巧【推荐】
May 13 Python
python怎么自定义捕获错误
Jun 29 Python
Python如何读写二进制数组数据
Aug 01 Python
matplotlib基础绘图命令之imshow的使用
Aug 13 Python
matplotlib subplot绘制多个子图的方法示例
Jul 28 #Python
python爬虫用mongodb的理由
Jul 28 #Python
python爬虫数据保存到mongoDB的实例方法
Jul 28 #Python
Python3爬虫带上cookie的实例代码
Jul 28 #Python
python实现批处理文件
Jul 28 #Python
Python3安装模块报错Microsoft Visual C++ 14.0 is required的解决方法
Jul 28 #Python
python爬虫请求头设置代码
Jul 28 #Python
You might like
MYSQL环境变量设置方法
2007/01/15 PHP
php-cli简介(不会Shell语言一样用Shell)
2013/06/03 PHP
采用ThinkPHP中F方法实现快速缓存实例
2014/06/13 PHP
php单态设计模式(单例模式)实例
2014/11/18 PHP
JCalendar 日历控件 v1.0 beta[兼容IE&Firefox] 有文档和例子
2007/05/30 Javascript
JQuery的ajax基础上的超强GridView展示
2009/09/18 Javascript
Extjs学习笔记之三 extjs form更多的表单项
2010/01/07 Javascript
JS面向对象编程 for Cookie
2010/09/19 Javascript
解决jquery的.animate()函数在IE6下的问题
2010/12/03 Javascript
JS跨域总结
2012/08/30 Javascript
javascript中为某个元素指定事件的三种方式
2014/08/07 Javascript
jquery实现上下左右滑动的方法
2015/02/09 Javascript
JQuery中两个ul标签的li互相移动实现方法
2015/05/18 Javascript
对象转换为原始值的实现方法
2016/06/06 Javascript
Bootstrap table表格简单操作
2017/02/07 Javascript
网页中右键功能的实现方法之contextMenu的使用
2017/02/20 Javascript
Angularjs自定义指令Directive详解
2017/05/27 Javascript
Vue.js 的移动端组件库mint-ui实现无限滚动加载更多的方法
2017/12/23 Javascript
vue2.0 资源文件assets和static的区别详解
2018/04/08 Javascript
JavaScript中引用vs复制示例详析
2018/12/06 Javascript
微信小程序左右滚动公告栏效果代码实例
2019/09/16 Javascript
详解Python编程中对Monkey Patch猴子补丁开发方式的运用
2016/05/27 Python
简单学习Python多进程Multiprocessing
2017/08/29 Python
flask session组件的使用示例
2018/12/25 Python
Python3 pip3 list 出现 DEPRECATION 警告的解决方法
2019/02/16 Python
python 使用while写猜年龄小游戏过程解析
2019/10/07 Python
css3中用animation的steps属性制作帧动画
2019/04/25 HTML / CSS
用canvas画心电图的示例代码
2018/09/10 HTML / CSS
美国时尚在线:Showpo
2017/09/08 全球购物
澳大利亚宠物食品和用品商店:PETstock
2020/01/02 全球购物
成功的酒店创业计划书
2013/12/27 职场文书
大学生个人先进事迹材料范文
2014/05/03 职场文书
节能环保家庭事迹材料
2014/08/27 职场文书
职工年度考核评语
2014/12/31 职场文书
如何写好活动总结
2019/06/21 职场文书
创业项目大全(适合在家创业的项目)
2019/08/15 职场文书