Python核心编程——多线程threading和队列
线程与进程类似不过他们是在同一个进程下执行的,并共享相同的上下文。可以将他们认为是在一个主进程或“主线程”中运行的迷你进程。
线程包括开始、执行顺序和结束三部分。它有一个指令指针,用于记录当前运行的上下文。当其他线程运行时,它可以被抢占(中断)和临时挂起(也称为睡眠)——这种做法叫做让步(yielding)。
threading模块知识概要
threading.Thread
Thread 是threading模块中最重要的类之一,可以使用它来创建线程。有两种方式来创建线程:一种是通过继承Thread类,重写它的run方法;另一种是创建一个threading.Thread对象,在它的初始化函数 (__init__)中将可调用对象作为参数传入。
- import threading
- import time
- '''
- 没有引入类,直接创建Thread实例,调用threading.Thread()方法
- '''
- def loop(i,sleep_time):
- print('loop:',i,'start')
- time.sleep(sleep_time)
- print('loop:',i,'done')
- def main():
- sleep_time = [4,2]
- loops = range(sleep_time.__len__())
- thread_list = []
- for i in loops:
- t = threading.Thread(target=loop,args=(i,sleep_time[i]))
- t.start()
- thread_list.append(t)
- for i in thread_list:
- i.join()#主线程等待所有子线程执行完毕再继续执行
- print('all thread have done!')
- if __name__ == "__main__":
- main()
调用方法引入线程
- import threading
- import time
- '''
- 引入类,实现了面向对象编程的思想,可扩展性强
- '''
- class threadFunc(object):
- def __init__(self,func,i,sleep_time):
- self.func = func
- self.i = i
- self.sleep_time = sleep_time
- def __call__(self, *args, **kwargs):#回调函数执行self.func(*args)
- self.func(self.i,self.sleep_time)
- def loop(i,sleep_time):
- print('loop:', i, 'start')
- time.sleep(sleep_time)
- print('loop:', i, 'done')
- def main():
- loops = [4,2]
- nloop = range(loops.__len__())
- thread_list = []
- for i in nloop:
- t = threading.Thread(target=threadFunc(loop,i,loops[i]))
- t.start()
- thread_list.append(t)
- for i in thread_list:
- i.join()
- print('all thread have done!')
- if __name__ == '__main__':
- main()
调用Thread函数实现面向对象思想
- import threading
- import time
- class Mythread(threading.Thread):
- def __init__(self,func,args,name = ''):
- '''
- 官方文档
- If a subclass overrides the constructor, it must make sure to invoke
- the base class constructor (Thread.__init__()) before doing anything
- else to the thread.
- 如果子类重写构造函数,则必须确保在对线程执行任何其他操作之前调用基类构造函数(Thread._init_())。
- '''
- threading.Thread.__init__(self)#继承父类的__init()__方法很重要
- self.func = func
- self.args = args
- self.name = name
- def run(self):
- self.func(*self.args)
- def loop(i,sleep_time):
- print('loop:', i, 'start')
- time.sleep(sleep_time)
- print('loop:', i, 'done')
- def main():
- loops = [4,2]
- thread_list = []
- nloop = range(len(loops))
- for i in nloop:
- t = Mythread(loop,(i,loops[i]),loop.__name__)
- t.start()
- thread_list.append(t)
- for i in thread_list:
- i.join()
- print('all have done!')
- if __name__ == '__main__':
- main()
通过继承实现线程的创建
Thread.join()
调用Thread.join将会使主调线程堵塞,直到被调用线程运行结束或超时。参数timeout是一个数值类型,表示超时时间,如果未提供该参数,那么主调线程将一直堵塞到被调线程结束。
- import threading, time
- def doWaiting():
- print 'start waiting:', time.strftime('%H:%M:%S')
- time.sleep(3)
- print 'stop waiting', time.strftime('%H:%M:%S')
- thread1 = threading.Thread(target = doWaiting)
- thread1.start()
- time.sleep(1)
- #确保线程thread1已经启动
- print 'start join'
- thread1.join()
- #将一直堵塞,直到thread1运行结束。
- print 'end join'
join()
threading.Lock与RLock
为什么要用锁这个控制机制?每个线程互相独立,相互之间没有任何关系,但是在同一个进程中的资源,线程是共享的,如果不进行资源的合理分配,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。所以不排除两个进程同时访问一个数据的情况发生,一旦发生就会造成数据修改错误。
比如下面这个例子:
- import threading
- import time
- gl_num = 0
- def show(arg):
- global gl_num
- time.sleep(1)
- gl_num +=1
- print (gl_num)
- for i in range(10):
- t = threading.Thread(target=show, args=(i,))
- t.start()
- print ('main thread stop')
- 运行结果如下
- main thread stop
- 123
- 45
- 6678
- 9
- Process finished
不加Lock,数据错误
- lock = threading.Lock()
- def show(arg):
- global gl_num
- lock.acquire()
- time.sleep(1)
- gl_num +=1
- lock.release()
- print (gl_num)
- thread_list = []
- for i in range(10):
- t = threading.Thread(target=show, args=(i,))
- t.start()
- thread_list.append(t)
- print ('main thread stop')
- 正常输出1-10
加入Lock
在threading模块中,定义两种类型锁:threading.Lock和threading.RLock。它们之间有一点细微的区别,通过比较下面两段代码来说明:
- import threading
- lock = threading.Lock()
- #Lock对象
- lock.acquire()
- lock.acquire()
- #产生了死琐。
- lock.release()
- lock.release()
- import threading
- rLock = threading.RLock()
- #RLock对象
- rLock.acquire()
- rLock.acquire()
- #在同一线程内,程序不会堵塞。
- rLock.release()
- rLock.release()
Lock与RLock
这两种锁的主要区别是:RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。注意:如果使用RLock,那么acquire和release必须成对出现,即调用了n次acquire,必须调用n次的release才能真正释放所占用的锁。
threading.Condition(书中没有。。。)
线程的挂起与阻塞:
挂起:是因为我们需要进行调度然后认为的暂停某个线程的执行,我们也会主动放下线程实现线程的继续运行
阻塞:多半是被动的,因为资源访问的竞争,或是线程冲突。
阻塞的线程挂起放下后依然是阻塞的。
可以把Condiftion理解为一把高级的锁,它提供了比Lock, RLock更高级的功能,允许我们能够控制复杂的线程同步问题。
threadiong.Condition在内部维护一个锁对象(默认是RLock),可以在创建Condigtion对象的时候把锁对象作为参数传入。
condition方法:
1.acquire():线程获得锁
2.release():释放锁
3.wait():线程挂起状态,会自动释放锁。
4.notify():通知其他阻塞的线程获得锁,notifyAll():通知所有阻塞的线程,并不会自动释放锁
经典案例:捉迷藏
- #encoding: utf-8
- import threading, time
- class Seeker(threading.Thread):
- def __init__(self, cond, name):
- threading.Thread.__init__(self)
- self.cond = cond
- self.name = name
- def run(self):
- time.sleep(1) # 1.确保seeker晚于hider开始执行
- print(1)
- self.cond.acquire() # 4. hider的锁释放了所以这里获得了锁
- print ('我把眼睛蒙上了')
- self.cond.notify() # 5.蒙上眼后通知hider,hider线程此时被唤醒并试图获取锁,但是锁还在seeker身上,所以hider被阻塞,seeker继续往下
- self.cond.wait() # 6. seeker锁被释放并且挂起,hider就获取锁开始继续往下运行了
- print(2)
- print ('我找到你了')
- self.cond.notify() # 9.找到了之后通知hider,hider意图获取锁但不行所以被阻塞,seeker往下
- self.cond.release() # 10.释放锁
- print(3)
- print ('我赢了')
- class Hider(threading.Thread):
- def __init__(self, cond, name):
- threading.Thread.__init__(self)
- self.cond = cond
- self.name = name
- def run(self):
- self.cond.acquire() # 2.hider获取锁
- self.cond.wait() # 3.hider被挂起然后释放锁
- print(4)
- print ('我已经藏好了')
- self.cond.notify() # 7.藏好后通知seeker,seeker意图获取锁,但是锁在hider身上所以seeker被阻塞
- self.cond.wait() # 8.hider被挂起,释放锁,seeker获取锁,seeker继续往下运行
- print(5)
- self.cond.release()# 11. 在此句之前一点,seeker释放了锁(#10),hider得到锁,随即这句hider释放锁
- print ('被你找到了')
- cond = threading.Condition()
- seeker = Seeker(cond, 'seeker')
- hider = Hider(cond, 'hider')
- seeker.start()
- hider.start()
捉迷藏
threading.Event(书中没有自行补充。。)
Event实现与Condition类似的功能,不过比Condition简单一点。它通过维护内部的标识符来实现线程间的同步问题。
Event.wait() : 堵塞线程,直到Event对象内部标识位被设为True或超时(如果提供了参数timeout)。
Event.clear() : 将标志位置于false状态。
Event.set() : 设置标志位为true
Event.isSet() : 判断标志位状态
用Event模拟红绿灯:
- import threading
- import time
- def car(event):
- while True:
- if event.isSet():
- print('green or yellow is on ,let`s go!')
- time.sleep(2)
- else:
- print('red in on ,we must stop!')
- time.sleep(2)
- def light(event):
- '''
- 红绿灯方法,红灯停,黄灯绿灯行
- :return:
- '''
- while True:
- event.clear()
- print("the light is red")
- time.sleep(6)
- event.set()
- print("the light is green")
- time.sleep(4)
- print("the light is yellow")
- time.sleep(2)
- def main():
- event = threading.Event()
- t1 = threading.Thread(target=light,args=(event,))
- t2 = threading.Thread(target=car,args=(event,))
- t1.start()
- t2.start()
- if __name__ == '__main__':
- main()
Event
- the light is red
- red in on ,we must stop!
- red in on ,we must stop!
- red in on ,we must stop!
- the light is green
- green or yellow is on ,let`s go!
- green or yellow is on ,let`s go!
- the light is yellow
- green or yellow is on ,let`s go!
- the light is red
- red in on ,we must stop!
- red in on ,we must stop!
- red in on ,we must stop!
- the light is green
- green or yellow is on ,let`s go!
- green or yellow is on ,let`s go!
- the light is yellow
- green or yellow is on ,let`s go!
- the light is red
- red in on ,we must stop!
- red in on ,we must stop!
- red in on ,we must stop!
- ...
运行结果
threading.BoundedSemaphore信号量
信号量是最古老的同步原语之一。它是一个计数器,当资源消耗时递减,当资源释放时递增。你可以认为信号量代表它们的资源可用或不可用。消耗资源使计数器递减的操作习惯上称为P() (来源于荷兰单词probeer/proberen),也称为wait、try、acquire、pend或procure。相对地,当一个线程对一个资源完成操作时,该资源需要返回资源池中。这个操作一般称为 V()(来源于荷兰单词 verhogen/verhoog),也称为 signal、increment、release、post、vacate。Python 简化了所有的命名,使用和锁的函数/方法一样的名字:acquire 和 release。信号量比锁更加灵活,因为可以有多个线程,每个线程拥有有限资源的一个实例。
- from threading import BoundedSemaphore,Lock,Thread
- import time
- import random
- baozi = BoundedSemaphore(3)
- lock = Lock()
- def producer():
- for i in range(random.randint(4,6)):
- lock.acquire()
- try:
- baozi.release()
- except Exception:
- print("包子普满啦")
- else:
- print('做了一个包子')
- lock.release()
- time.sleep(random.randint(1,6))
- def customer():
- for i in range(random.randint(3,7)):
- lock.acquire()
- try:
- baozi.acquire(blocking=False)
- except Exception:
- print('包子铺没有包子啦')
- else:
- print('买了一个包子')
- lock.release()
- time.sleep(random.randint(3, 6))
- def main():
- thread_list = []
- pro = Thread(target=producer)
- thread_list.append(pro)
- cus = Thread(target=customer)
- thread_list.append(cus)
- for i in thread_list:
- i.start()
- for i in thread_list:
- i.join()
- if __name__ == '__main__':
- main()
- '''
- a = BoundedSemaphore(0)
- if a.acquire(blocking=False):
- print('ok')
- else:
- print("empty")
- '''
信号量
以上就是threading模块我目前所能掌握,一点点写出来的部分了。
队列Queue
队列是线程安全的!!!
首先,队列有很多种,根据进出顺序来分类,可以分成
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之后,主线程就不会因为队列空了而擅自结束,而是等待最后那个线程处理完成了。
队列实现生产者消费者
- import threading
- import time
- import queue
- q = queue.Queue(maxsize=10)
- def producer(name): # 生产者
- count = 1
- while True:
- q.put("包子%s" % count)
- print("生产了包子", count)
- count += 1
- time.sleep(0.5)
- def consumer(name): # 消费者
- while True:
- print("[%s]取到[%s]并且吃了它..." % (name, q.get()))
- time.sleep(1)
- p = threading.Thread(target=producer, args=("Jerry",))
- c1 = threading.Thread(target=consumer, args=("Tom",))
- c2 = threading.Thread(target=consumer, args=("Tony",))
- p.start()
- c1.start()
- c2.start()
queue_pro_cus
队列与condition组合实现生产者消费者
- import queue
- import threading
- import time
- from threading import Condition
- class cus_thread(threading.Thread):
- def __init__(self,queue,con):
- threading.Thread.__init__(self);
- self.queue = queue
- self.con = con
- def run(self):
- time.sleep(1)
- q = self.queue
- while True:
- self.con.acquire()
- if not q.empty():
- task = q.get()
- print('get task',task)
- self.con.notify()
- self.con.release()
- time.sleep(1)
- else:
- print("no task")
- self.con.wait()
- class pro_thread(threading.Thread):
- def __init__(self,queue,con):
- threading.Thread.__init__(self)
- self.queue = queue
- self.con = con
- def run(self):
- q = self.queue
- while True:
- self.con.acquire()
- if q.empty():
- q.put('')
- print("add a job")
- self.con.notify()
- self.con.release()
- time.sleep(2)
- else:
- self.con.wait()
- def main():
- con = Condition()
- thread_list = []
- q = queue.Queue()
- pro = pro_thread(q,con)
- thread_list.append(pro)
- cus = cus_thread(q,con)
- thread_list.append(cus)
- for i in thread_list:
- i.start()
- if __name__ == '__main__':
- main()
queue_condition
Python核心编程——多线程threading和队列的更多相关文章
- python核心编程第二版笔记
python核心编程第二版笔记由网友提供:open168 python核心编程--笔记(很详细,建议收藏) 解释器options:1.1 –d 提供调试输出1.2 –O 生成优化的字节码(生成 ...
- python核心编程--笔记
python核心编程--笔记 的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找pyt ...
- python核心编程--笔记(不定时跟新)(转)
的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找python路径 1.4 –v ...
- python核心编程笔记(转)
解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找python路径 1.4 –v 冗 ...
- Python核心编程第二版(中文).pdf 目录整理
python核心编程目录 Chapter1:欢迎来到python世界!-页码:7 1.1什么是python 1.2起源 :罗萨姆1989底创建python 1.3特点 1.3.1高级 1.3.2面向 ...
- python并发编程&多线程(二)
前导理论知识见:python并发编程&多线程(一) 一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性 官网链 ...
- Python核心编程(第二版)PDF
Python核心编程(第二版) 目录 第1部分 Python核心第1章 欢迎来到Python世界1.1 什么是Python1.2 起源1.3 特点1.3.1 高级1.3.2 面向对象1.3.3 可升级 ...
- Python核心编程(第3版)PDF高清晰完整中文版|网盘链接附提取码下载|
一.书籍简介<Python核心编程(第3版)>是经典畅销图书<Python核心编程(第二版)>的全新升级版本.<Python核心编程(第3版)>总共分为3部分.第1 ...
- 学习《Python核心编程》做一下知识点提要,方便复习(一)
学习<Python核心编程>做一下知识点提要,方便复习. 计算机语言的本质是什么? a-z.A-Z.符号.数字等等组合成符合语法的字符串.供编译器.解释器翻译. 字母组合后产生各种变化拿p ...
随机推荐
- (linux)安装redis---简装
redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统.和Memcached类似,但很大程度补偿了memcached的不足,它支持存储的value类型相对更多,包括strin ...
- CodeForces 114B 【STL应用】
思路: 原来string类能sort 和 swap....太强了.... 注意:字典序最小输出,因为某个地方写挫了,sort了n发,代码挫. #include <bits/stdc++.h> ...
- PAT12-012【建最小堆】
卧槽..没看清 值 还有负的.. PS: 注意他说是一个一个插入的,也就是插在完全二叉树的最末位置,然后向上更新. #include<bits/stdc++.h> using namesp ...
- java 提取(解压)zip文件中特定后缀的文件并保存到指定目录
内容简介 本文主要介绍使用ZipFile来提取zip压缩文件中特定后缀(如:png,jpg)的文件并保存到指定目录下. 导入包:import java.util.zip.ZipFile; 如需添加对r ...
- 创建、配置Servlet
1.创建Servlet 2.选择继承的类及需要覆盖的方法 3.Servlet结构 package com.sysker.servlet; import java.io.IOException; imp ...
- 简单的vue.js的表单提交数据至flask然后数据库入库,再将表里面的数据展示在网页
一.先在数据库中创建表格 在mysql中建users库并建立一个含有ID,username,email三个字段的user表 二.去vue的组件里面写页面的表单代码,注意form标签里的action需要 ...
- Java基础笔记(十一)—— 字符串与数组
字符串的声明与初始化主要两种:String s1=new String("abc"); 或 String s2="abc"; String ...
- 理解js继承的6种方式
想要继承,就必须要提供个父类(继承谁,提供继承的属性) 一.原型链继承 重点:让新实例的原型等于父类的实例. 特点:1.实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性.(新 ...
- 打造H5动感影集的爱恨情仇–动画性能篇
“你听说过动感影集么?” 动感影集是QQ空间新功能,可以将静态的图片轻松转变为动态的视频集,且载体是HTML5(简称H5)页面,意味着可以随时分享到空间或朋友圈给好友欣赏! 移动端区别于PC年代的相册 ...
- 我在B站学习 Javascript入门教程 基础
B站av9243452的一系列视频,适合学过其他编程语言的人观看,还挺不错的 共43节,该随笔为1~16节 Js介绍 如需使用外部文件,请在 <script> 标签的 "src& ...