python 多线程并发threading & 任务队列Queue
https://docs.python.org/3.7/library/concurrency.html
python程序默认是单线程的,也就是说在前一句语句执行完之前后面的语句不能继续执行
先感受一下线程,一般情况下:
def testa():
sleep(1)
print "a" def testb():
sleep(1)
print "b" testa()
testb()
#先隔出一秒打印出a,再过一秒打出b
但是如果用了threading的话:
ta = threading.Thread(target=testa)
tb = threading.Thread(target=testb)
for t in [ta,tb]:
t.start()
for t in [ta,tb]:
t.join()
print "DONE" #输出是ab或者ba(紧贴着的)然后空一行再来DONE的结果。
得到这样的结果是因为这样的,在start之后,ta首先开始跑,但是主线程(脚本本身)没有等其完成就继续开始下一轮循环,然后tb也开始了,在之后的一段时间里,ta和tb两条线程(分别代表了testa和testb这两个过程)共同执行。相对于一个个迭代而言,这样做无疑是大大提高了运行的速度。
Thread类为线程的抽象类,其构造方法的参数target指向一个函数对象,即该线程的具体操作。此外还可以有args=<tuple>来给target函数传参数。需要注意的是当传任何一个序列进去的话Thread会自动把它分解成单个单个的元素然后分解传给target函数。我估计在定义的时候肯定是*args了。
join方法是个很tricky的东西,至今还不是很清楚地懂这是个什么玩意儿。join([timeout])方法阻塞了主线程,直到调用此方法的子线程完成之后主线程才继续往下运行。(之前我糊里糊涂地把join就紧紧接在start后面写了,如果这么写了的话那么多线程在速度上就毫无优势,和单线程一样了= =)。而像上面这个示例一样,先一个遍历把所有线程 都启动起来,再用一个遍历把所有线程都join一遍似乎是比较通行的做法。
关于线程锁
多线程程序涉及到一个问题,那就是当不同线程要对同一个资源进行修改或利用时会出现混乱,所以有必要引入线程锁。
可以通过Thread.Lock类来创建简单的线程锁。lock = threading.Lock()即可。在某线程start之前,让lock.acquire(),且lock在acquire()之后不能再acquire,否则会报错。当线程结束后调用lock.release()来释放锁就好了。一般而言,有锁的多线程场景可以提升一部分效率,但在写文件等时机下会有阻塞等待的情况。相比之下,无所多线程场景可以进一步提升效率,但是可能会引起读写冲突等问题,所以要慎用。一定要确认各个线程间没有共同的资源之类的问题后再实行无锁多线程。
● 以上的包装线程的方式是一种面向过程的方法,下面介绍一下如何面向对象地来抽象线程
面向对象地抽象线程需要自定义一个类继承Thread类。比如自定义class MyThread(Thread)。这个类的一个实例就是代表了一个线程,然后通过重载这个类中的run方法(是run,不是start!!但start的动作确实就是调用run)来执行具体的操作。此时锁可以作为一个构造方法的参数,将一个锁传进不同的实例中以实现线程锁控制。比如:
#方法二:从Thread继承,并重写run()
class MyThread(threading.Thread):
def __init__(self,arg):
super(MyThread, self).__init__()#注意:一定要显式的调用父类的初始化函数。
self.arg=arg
def run(self):#定义每个线程要运行的函数
time.sleep(1)
print 'the arg is:%s\r' % self.arg for i in xrange(4):
t =MyThread(i)
t.start() print 'main thread end!'
Thread类还有以下的一些方法,自定义的类也可以调用
getName()
setName(...) //其实Thread类在构造方法中有一个name参数,可以为相应的线程取一个名字。这两个方法就是相关这个名字属性的
isAlive() 一个线程从start()开始到run()结束的过程中没有异常,则其实alive的。
setDaemon(True/False) 是否设置一个线程为守护线程。当你设置一个线程为守护线程之后,程序不会等待这个线程结束再退出程序,可参考http://blog.csdn.net/u012063703/article/details/51601579
● 除了Thread类,threading中还有以下一些属性,简单介绍一下:
Timer类,Timer(int,target=func) 和Thread类类似,只不过它在int秒过后才以target指定的函数开始线程运行
currentThread() 获得当前线程对象
activeCount() 获得当前活动的线程总个数
enumerate() 获得所有活动线程的列表
settrace(func) 设置一跟踪函数,在run执行前执行
setprofile(func) 设置一跟踪函数,在run执行完毕之后执行
Queue用于建立和操作队列,常和threading类一起用来建立一个简单的线程队列。
首先,队列有很多种,根据进出顺序来分类,可以分成
Queue.Queue(maxsize) FIFO(先进先出队列)
Queue.LifoQueue(maxsize) LIFO(先进后出队列)
Queue.PriorityQueue(maxsize) 为优先度越低的越先出来
如果设置的maxsize小于1,则表示队列的长度无限长
FIFO是常用的队列,其一些常用的方法有:
Queue.qsize() 返回队列大小
Queue.empty() 判断队列是否为空
Queue.full() 判断队列是否满了
Queue.get([block[,timeout]]) 从队列头删除并返回一个item,block默认为True,表示当队列为空却去get的时候会阻塞线程,等待直到有有item出现为止来get出这个item。如果是False的话表明当队列为空你却去get的时候,会引发异常。在block为True的情况下可以再设置timeout参数。表示当队列为空,get阻塞timeout指定的秒数之后还没有get到的话就引发Full异常。
Queue.put(...[,block[,timeout]]) 向队尾插入一个item,同样若block=True的话队列满时就阻塞等待有空位出来再put,block=False时引发异常。同get的timeout,put的timeout是在block为True的时候进行超时设置的参数。
Queue.task_done() 从场景上来说,处理完一个get出来的item之后,调用task_done将向队列发出一个信号,表示本任务已经完成
Queue.join() 监视所有item并阻塞主线程,直到所有item都调用了task_done之后主线程才继续向下执行。这么做的好处在于,假如一个线程开始处理最后一个任务,它从任务队列中拿走最后一个任务,此时任务队列就空了但最后那个线程还没处理完。当调用了join之后,主线程就不会因为队列空了而擅自结束,而是等待最后那个线程处理完成了。
结合threading和Queue可以构建出一个简单的生产者-消费者模型,比如:
import threading
import Queue
import time
class worker(threading.Thread):
def __init__(self,queue):
threading.Thread.__init__(self)
self.queue=queue
self.thread_stop=False def run(self):
while not self.thread_stop:
print("thread%d %s: waiting for tast" %(self.ident,self.name))
try:
task=q.get(block=True, timeout=20)#接收消息
except Queue.Empty:
print("Nothing to do!i will go home!")
self.thread_stop=True
break
print("task recv:%s ,task No:%d" % (task[0],task[1]))
print("i am working")
time.sleep(3)
print("work finished!")
q.task_done()#完成一个任务
res=q.qsize()#判断消息队列大小
if res>0:
print("fuck!There are still %d tasks to do" % (res)) def stop(self):
self.thread_stop = True if __name__ == "__main__":
q=Queue.Queue(3)
worker=worker(q)
worker.start()
q.put(["produce one cup!",1], block=True, timeout=None)#产生任务消息
q.put(["produce one desk!",2], block=True, timeout=None)
q.put(["produce one apple!",3], block=True, timeout=None)
q.put(["produce one banana!",4], block=True, timeout=None)
q.put(["produce one bag!",5], block=True, timeout=None)
print("***************leader:wait for finish!")
q.join()#等待所有任务完成
print("***************leader:all task finished!")
输出是这样的
thread139958685849344 Thread-1: waiting for tast 1
task recv:produce one cup! ,task No:1
i am working
work finished!
fuck!There are still 3 tasks to do
thread139958685849344 Thread-1: waiting for tast 1
task recv:produce one desk! ,task No:2
i am workingleader:wait for finish!
work finished!
fuck!There are still 3 tasks to do
thread139958685849344 Thread-1: waiting for tast 1
task recv:produce one apple! ,task No:3
i am working
work finished!
fuck!There are still 2 tasks to do
thread139958685849344 Thread-1: waiting for tast 1
task recv:produce one banana! ,task No:4
i am working
work finished!
fuck!There are still 1 tasks to do
thread139958685849344 Thread-1: waiting for tast 1
task recv:produce one bag! ,task No:5
i am working
work finished!
thread139958685849344 Thread-1: waiting for tast 1
***************leader:all task finished!
Nothing to do!i will go home!
上例中并没有性能的提升(毕竟还是只有一个线程在跑)。线程队列的意义并不是进一步提高运行效率,而是使线程的并发更加有组织。可以看到,在增加了线程队列之后,程序对于线程的并发数量就有了控制。新线程想要加入队列开始执行,必须等一个既存的线程完成之后才可以。举个例子,比如
for i in range(x):
t = MyThread(queue)
t.start()
x在这里是个变量,我们不知道这个循环会触发多少线程并发,如果多的话就会很冒险。但是有了队列之后,把一个队列作为所有线程构建线程对象时的一个参数,让线程必须按照这个队列规定的大小来执行的话,就不担心过多线程带来的危险了。
python 多线程并发threading & 任务队列Queue的更多相关文章
- 【Python】 多线程并发threading & 任务队列Queue
threading python程序默认是单线程的,也就是说在前一句语句执行完之前后面的语句不能继续执行(不知道我理解得对不对) 先感受一下线程,一般情况下: def testa(): sleep(1 ...
- 用Queue控制python多线程并发数量
python多线程如果不进行并发数量控制,在启动线程数量多到一定程度后,会造成线程无法启动的错误. 下面介绍用Queue控制多线程并发数量的方法(python3). # -*- coding: utf ...
- Python多线程(threading模块)
线程(thread)是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. ...
- Python 多线程并发程序设计与分析
多线程并发程序设计与分析 by:授客 QQ:1033553122 1.技术难点分析与总结 难点1:线程运行时,运行顺序不固定 难点2:同一段代码,再不加锁的情况下,可能被多个线程同时执行,这会造成很多 ...
- Python多线程笔记(三),queue模块
尽管在Python中可以使用各种锁和同步原语的组合编写非常传统的多线程程序,但有一种首推的编程方式要优于其他所有编程方式即将多线程程序组织为多个独立人物的集合,这些任务之间通过消息队列进行通信 que ...
- Python多线程并发的误区
由于项目要做一个并发测试,由于断言的东西较多,决定手写脚本.于是用python写了脚本: def test_method(thread_no): print("%s===test_metho ...
- Python多线程(3)——Queue模块
Queue模块支持先进先出(FIFO)队列,支持多线程的访问,包括一个主要的类型(Queue)和两个异常类(exception classes). Python 2 中的Queue模块在Python ...
- python多线程与threading模块
python多线程与_thread模块 中介绍了线程的基本概念以及_thread模块的简单示例.然而,_thread模块过于简单,使得我们无法用它来准确地控制线程,本文介绍threading模块,它提 ...
- python多线程--优先级队列(Queue)
Python的Queue模块中提供了同步的.线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列PriorityQueue.这些队列都实现 ...
随机推荐
- 【插件开发】—— 14 Site is incorrect!编辑器启动报错!
前言 博文纵览 最近在弄编辑器的时候出现了一个十分尴尬的错误!这里收录一下: BUG如下图所示: 目测堆栈,与自己开发的代码无关.完全是Eclipse自己初始化的时候遇到了问题,最头疼的就是这种问题. ...
- jquery的validate的用法
//引入js文件 //jquery 文件 <script src="__PUBLIC__/static/wap/js/jquery.min.js?v=2.1.4">&l ...
- react-native页面之间的相互传值
react-native页面之间的相互传值 之前在自己学习react-native的时候,在页面跳转和传值问题上花了一段时间去网上搜索和查找资料,自己总结了两个方法.可以参考 https://blog ...
- header的参数不能带下划线
移动端把一些公共参数放在了 header 了, 在 laravel 中使用 use \Illuminate\Http\Request; //这个是获取所有header信息Request::header ...
- [ZPG TEST 105] 扑克游戏【Huffman】
扑克游戏 (poker) 题目描述: 有一棵无穷大的满二叉树,根为star,其余所有点的权值为点到根的距离,如图: 现在你有一些扑克牌,点数从1到13,你要把这些扑克牌全部放到这个树上: 当你把点数为 ...
- 菜鸡CodeFoces打卡单
2017.12.13 1:05 签到3题 Educational Codeforces Round 34 (Rated for Div. 2) 补题:
- 415 Add Strings 字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和.注意: num1 和num2 的长度都小于 5100. num1 和num2 都只包含数字 0-9. num1 和 ...
- python_基础部分(1)
第1章 Python介绍 1.1 基础介绍 l 代码:代码的出现是为了解决生活中的问题 l 编译解释器:目的是让解释器将代码翻译成计算机可识别的语言 l 编程语言:按照一定规则写出来的语言, C ...
- Android开发学习——Android Studio配置SVN
一.基本配置 1. 下载这个,然后双击 安装,按下图这样选 然后 傻瓜式安装 2. 进入Android studio设置:Use Command Line Client 选择浏览到第1步你本地安装 T ...
- Jquery操作常用表单元素
由于对前端的东西不是很熟练,导致jquery操作表单的东西总是忘记,每次用都要查一下,效率低下,记录下来,以便下次使用. CheckBox checkbox改变事件 $('#IsAllSearch') ...