Python之路【第八篇】python实现线程池
线程池概念
什么是线程池?
诸如web服务器、数据库服务器、文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务。
构建服务器应用程序的一个过于简单的模型是:每当一个请求到达就创建一个新的服务对象,然后在新的服务对象中为请求服务。
但当有大量请求并发访问时,服务器不断的创建和销毁对象的开销很大。
所以提高服务器效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这样就引入了“池”的概念,
“池”的概念使得人们可以定制一定量的资源,然后对这些资源进行复用,而不是频繁的创建和销毁。
线程池是预先创建线程的一种技术。
这些线程都是处于睡眠状态,即均为启动,不消耗CPU,而只是占用较小的内存空间。
当请求到来之后,缓冲池给这次请求分配一个空闲线程,把请求传入此线程中运行,进行处理。
当预先创建的线程都处于运行状态,即预制线程不够,线程池可以自由创建一定数量的新线程,用于处理更多的请求。
当系统比较闲的时候,也可以通过移除一部分一直处于停用状态的线程。
线程池的注意事项
虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。在使用线程池时需注意线程池大小与性能的关系,注意并发风险、死锁、资源不足和线程泄漏等问题。
1、线程池大小。多线程应用并非线程越多越好,需要根据系统运行的软硬件环境以及应用本身的特点决定线程池的大小。
一般来说,如果代码结构合理的话,线程数目与CPU 数量相适合即可。
如果线程运行时可能出现阻塞现象,可相应增加池的大小;如有必要可采用自适应算法来动态调整线程池的大小,以提高CPU 的有效利用率和系统的整体性能。
2、并发错误。多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。
3、线程泄漏。这是线程池应用中一个严重的问题,当任务执行完毕而线程没能返回池中就会发生线程泄漏现象。
线程池要点
线程池要点:
线程池要点: 1、通过判断等待的任务数量和线程池中的最大值,取最小值来判断开启多少线程来工作 比如: 任务数是3,进程池最大20 ,那么咱们只需要开启3个线程就行了。 任务数是500,进程池是20,那么咱们只开20个线程就可以了。 取最小值2、实现线程池正在运行,有一个查看的功能,查看一下现在线程里面活跃的线程是多少等待的是多少?线程总共是多少,等待中多少,正在运行中多少 作用: 方便查看当前线程池状态 能获取到这个之后就可以当线程一直处于空闲状态查看状态用:上下文管理来做,非常nice的一点3、关闭线程
简单线程池实现
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luo_t' import Queue import threading import time''' 这个简单的例子的想法是通过: 1、利用Queue特性,在Queue里创建多个线程对象 2、那我执行代码的时候,去queue里去拿线程! 如果线程池里有可用的,直接拿。 如果线程池里没有可用,那就等。 3、线程执行完毕,归还给线程池 '''class ThreadPool(object): #创建线程池类def __init__(self,max_thread=20):#构造方法,设置最大的线程数为20self.queue = Queue.Queue(max_thread) #创建一个队列for i in xrange(max_thread):#循环把线程对象加入到队列中 self.queue.put(threading.Thread)#把线程的类名放进去,执行完这个Queuedef get_thread(self):#定义方法从队列里获取线程return self.queue.get()def add_thread(self):#定义方法在队列里添加线程 self.queue.put(threading.Thread)pool = ThreadPool(10)def func(arg,p):print argtime.sleep(2)p.add_thread() #当前线程执行完了,我在队列里加一个线程!for i in xrange(300):thread = pool.get_thread() #线程池10个线程,每一次循环拿走一个!默认queue.get(),如果队列里没有数据就会等待。t = thread(target=func,args=(i,pool))t.start()''' self.queue.put(threading.Thread) 添加的是类不是对象,在内存中如果相同的类只占一份内存空间 并且如果这里存储的是对象的话每次都的新增都得在内存中开辟一段内存空间还有如果是对象的话:下面的这个语句就不能这么调用了! for i in xrange(300):thread = pool.get_thread()t = thread(target=func,args=(i,pool))t.start()通过查看源码可以知道,在thread的构造函数中:self.__args = args self.__target = target 都是私有字段那么调用就应该这么写for i in xrange(300):ret = pool.get_thread()ret._Thread__target = funcret._Thread__args = (i,pool)ret.start() '''
simple_pool.py
复杂线程池需要知道的知识点
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luo_t'import Queue obj = object() #object也是一个类,我创建了一个对象obj q = Queue.Queue() for i in range(10):print id(obj)#看萝卜号 q.put(obj) ''' 这个队列里有10个萝卜(萝卜=obj),但是这10个萝卜只是个投影。 我们在for循环的时候put到队列里,obj有变化吗?是否有新开辟空间?显然没有 '''
knowledge_point_1.py
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luo_t' import contextlib import threading import time import randomdoing = [] def number(l2):while True:print len(l2)time.sleep(1)t = threading.Thread(target=number,args=(doing,)) #开启一个线程,每一秒打印列表,当前工作中的线程数量 t.start()#添加管理上下文的装饰器 @contextlib.contextmanager def show(li,iterm):li.append(iterm)yield'''yield冻结这次操作,就出去了,with就会捕捉到,然后就会执行with下的代码块,当with下的代码块执行完毕后就会回来继续执行yield下面没有执行的代码块!然后就执行完毕了如果with代码块中的非常耗时,那么doing的长度是不是一直是1,说明他没执行完呢?我们就可以获取到正在执行的数量,当他with执行完毕后执行yield的后续的代码块。把他移除后就为0了!'''li.remove(iterm)def task(arg):with show(doing,1):#通过with管理上下文进行切换print len(doing)time.sleep(10) #等待10秒这里可以使用random模块来操作~for i in range(20): #开启20个线程执行temp = threading.Thread(target=task,args=(i,))temp.start()''' 作用:我们要记录正在工作的的列表 比如正在工作的线程我把加入到doing这个列表中,如果工作完成的把它从doing列表中移除。 通过这个机制,就可以获取现在正在执行的线程都有多少 '''
knowledge_point_2.py
线程池实现
#!/usr/bin/env python #-*- coding:utf-8 -*- __author__ = 'luo_t' from Queue import Queue import contextlib import threadingWorkerStop = object()class ThreadPool:workers = 0threadFactory = threading.ThreadcurrentThread = staticmethod(threading.currentThread)def __init__(self, maxthreads=20, name=None):self.q = Queue(0) #这里创建一个队列,如果是0的话表示不限制,现在这个队列里放的是任务self.max = maxthreads #定义最大线程数self.name = nameself.waiters = []#这两个是用来计数的self.working = []#这两个是用来技术的def start(self):#self.max 最大线程数#q.qisze(),任务个数needSize = self.q.qsize()while self.workers < min(self.max, needSize):#min(10,20)取最小值#wokers默认为0 【workers = 0】'''举例来说:while self.workers < min(self.max, needSize):这个循环,比如最大线程为20,咱们的任务个数为10,取最小值为10每次循环开1个线程,并且workers自增1,那么循环10次后,开了10个线程了workers = 10 ,那么workers就不小于10了就不开线程了,我线程开到最大了,你们这10个线程去消耗这10个任务去吧并且这里不阻塞,创建完线程就去执行了!每一个线程都去执行_worker方法去了'''self.startAWorker()def startAWorker(self):self.workers += 1newThread = self.threadFactory(target=self._worker, name='shuaige') #创建一个线程并去执行_worker方法 newThread.start()def callInThread(self, func, *args, **kw):self.callInThreadWithCallback(None, func, *args, **kw)def callInThreadWithCallback(self, onResult, func, *args, **kw):o = (func, args, kw, onResult)self.q.put(o)@contextlib.contextmanagerdef _workerState(self, stateList, workerThread):stateList.append(workerThread)try:yieldfinally:stateList.remove(workerThread)def _worker(self):ct = self.currentThread()o = self.q.get() #去队列里取任务,如果有任务就O就会有值,每个任务是个元组,有方法,有参数while o is not WorkerStop:with self._workerState(self.working, ct): #上下文切换function, args, kwargs, onResult = odel otry:result = function(*args, **kwargs)success = Trueexcept:success = Falseif onResult is None:passelse:passdel function, args, kwargsif onResult is not None:try:onResult(success, result)except:#context.call(ctx, log.err)passdel onResult, resultwith self._workerState(self.waiters, ct): #当线程工作完闲暇的时候,在去取任务执行o = self.q.get()def stop(self): #定义关闭线程方法while self.workers: #循环workers值self.q.put(WorkerStop) #在队列中增加一个信号~self.workers -= 1 #workers值-1 直到所有线程关闭def show(arg):import timetime.sleep(1)print argpool = ThreadPool(10)#创建500个任务,队列里添加了500个任务 #每个任务都是一个元组(方法名,动态参数,动态参数,默认为NoNe) for i in range(100):pool.callInThread(show, i)pool.start() #队列添加完成之后,开启线程让线程一个一个去队列里去拿 pool.stop() #当上面的任务都执行完之后,线程中都在等待着在队列里去数据呢! ''' 我们要关闭所有的线程,执行stop方法,首先workers这个值是当前的线程数量,我们给线程发送一个信号“WorkerStop” 在线程的工作里: while o is not WorkerStop: 如果线程获取到这个值就不执行了,然后这个线程while循环就停止了,等待 python的垃圾回收机制,回收。然后在self.workers -= 1 ,那么所有的线程收到这个信号之后就会停止!!! over~ '''
更多请参考:http://www.cnblogs.com/wupeiqi/articles/4839959.html
转载于:https://www.cnblogs.com/luotianshuai/p/5131001.html
Python之路【第八篇】python实现线程池相关推荐
- python内置函数返回序列中最大元素_Python之路(第八篇)Python内置函数、zip()、max()、min()...
一.python内置函数 abs() 求绝对值 例子 print(abs(-2)) all() 把序列中每一个元素做布尔运算,如果全部都是true,就返回true, 但是如果是空字符串.空列表也返回t ...
- Python之路【第一篇】:Python简介和入门
Python之路[第一篇]:Python简介和入门 Python简介 Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗 ...
- Python之路【第二篇】:Python基础(一)
Python之路[第二篇]:Python基础(一) 入门知识拾遗 一.作用域 对于变量的作用域,执行声明并在内存中存在,该变量就可以在下面的代码中使用. 1 2 3 if 1==1: name ...
- Python和Java结合的项目实战_[项目实战] Python高级教程项目实战篇 Python和Java结合的项目实战 视频教程 [...
资源介绍 课程简介:xa0xa0 Python高级教程项目实战篇 Python和Java结合的项目实战 视频教程 教学视频 ----------------------课程目录 Python项目实战篇 ...
- 原创:《Python之路实战40篇》PDF高清版,限时下载!
内容简介 1 <Python之路40篇实战 .pdf>包含Python篇.数据分析篇.机器学习篇等 3 大模块,目录如下: 下载方式 2 关注公众号「Python与算法社区」,后台回复一个 ...
- Python开发【第八篇】:网络编程 Socket
Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...
- Python之路(第十七篇)logging模块
一.logging模块 (一).日志相关概念 日志是一种可以追踪某些软件运行时所发生事件的方法.软件开发人员可以向他们的代码中调用日志记录相关的方法来表明发生了某些事情.一个事件可以用一个可包含可选变 ...
- Python金融系列第八篇:Fama-French 多因子模型
作者:chen_h 微信号 & QQ:862251340 微信公众号:coderpai 第一篇:计算股票回报率,均值和方差 第二篇:简单线性回归 第三篇:随机变量和分布 第四篇:置信区间和假设 ...
- python之路day5_Python语言之python之路笔记day19
本文主要向大家介绍了Python语言之python之路笔记day19,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. 一.Django请求生命周期 对于所有的web框架来说本质就是 ...
- Python 学习笔记 第三篇 Python实现网易云评论网页爬虫+词云展示 (Pycharm+Mysql)
初始条件,具体可见我的其他文章. 1.安装Python.Python 学习笔记 第一篇 Python的安装与配置 2.安装Pycharm,并导入第三方包.Python 学习笔记 第二篇 Python ...
最新文章
- Android 取得 ListView中每个Item项目的值
- thinkpad 61系机器(T61 X61/X61S R61)休眠掉电问题解决办法
- 可视化深入理解损失函数与梯度下降 | 技术头条
- 用Python从头实现一个神经网络
- webpack 相关文章
- 在Sqlserver下巧用行列转换日期的数据统计
- EXCLE图形插入实例
- python教学数据库_Python学习之数据库初识
- Walle 瓦力 web部署系统
- java上传excel文件代码,求java把上传文件的excel表中数据存入数据库中.实现录入的代码?...
- FusionCharts V3图表导出图片和PDF属性说明
- ajax中data传参报错
- 十几个NPM恶意包劫持 Discord 服务器
- Linux添加/删除用户和用户组(linux中,添加cvs用户,实质就是添加linux用户。)
- Java 并发:volatile 关键字解析
- MySQL命令行乱码问题的解决
- 几个比较好的app开发框架
- c语言程序设计课程表,c语言怎样编写课程表,请问程序怎样写?
- python爬虫-代理池的维护
- Excel排位函数RANK的使用方法