线程
def sayhi(num): print("running on number:%s" % num) time.sleep(3) print("子线程结束!") t1 = threading.Thread(target=sayhi, args=(1,)) t2 = threading.Thread(target=sayhi, args=(2,)) t1.start() t2.start() print("end!!")
class MyThread(threading.Thread): def __init__(self, num): threading.Thread.__init__(self) self.num = num def run(self): # 定义每个线程要运行的函数(继承式调用必需自己重写run函数) print("running on number:%s" % self.num) time.sleep(3) t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start()
线程对象方法 # run(): 线程被cpu调度后自动执行线程对象的run方法 # start():启动线程活动。 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
thread_obj.join():在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
def sayhi(num): print("running on number:%s" % num) time.sleep(3) print("子线程结束!") t1 = threading.Thread(target=sayhi, args=(1,)) t1.start() t1.join print("end!")
守护线程:只要主线程完成了,不管子线程是否完成,都要和主线程一起退出
thread_obj.setDaemon(True):将线程声明为守护线程,必须在start() 方法调用之前设置
def sayhi(num): print("running on number:%s" % num) time.sleep(3) print("子线程结束!") t1 = threading.Thread(target=sayhi, args=(1,)) t.setDaemon(True) t1.start() print("end!")
串行,并行,并发
串行:顺序执行
并行 : 指两者同时执行
并发 : 指资源有限的情况下,两者交替轮流使用资源
同步和异步
同步:一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,要么成功都成功,失败都失败,两个任务的状态可以保持一致。
异步:不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。
阻塞与非阻塞
阻塞:等待的时候不能做其它事情
非阻塞:等待的时候可以做其它事情
GIL锁
python 解释器的GIL锁导致其在多线程在同一时间只能有一个线程被执行
IO密集形任务:python适用
计算密集形任务:python表现不佳(GIL锁导致)
同步锁
import time import threading def addNum(): global num # 在每个线程中都获取这个全局变量 # num-=1 temp = num # print('--get num:',num ) time.sleep(0.1) num = temp - 1 # 对此公共变量进行-1操作 num = 100 # 设定一个共享变量 thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: # 等待所有线程执行完毕 t.join() print('final num:', num) # print('可以看到本例当时间轮询足够短时,每次执行的结果都可能不一样') print("因为多个线程都在同时操作同一个共享资源,造成了资源破坏")
锁通常被用来实现对共享资源的同步访问。为每一个共享资源创建一个Lock对象,当你需要访问该资源时,调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放),待资源访问完后,再调用release方法释放锁
import threading Sync_lock=threading.Lock() Sync_lock.acquire() '''对公共数据的操作''' Sync_lock.release()
import time import threading Sync_lock = threading.Lock() def addNum2(): global num2 Sync_lock.acquire() # 调用acquire方法来获取锁对象(如果其它线程已经获得了该锁,则当前线程需等待其被释放) # 加锁的代码串行运行 temp = num2 time.sleep(0.001) num2 = temp - 1 # 加锁的代码串行运行 Sync_lock.release() # 待资源访问完后,再调用release方法释放锁 num2 = 100 # 设定一个共享变量 thread_list = [] for i in range(100): t = threading.Thread(target=addNum2) t.start() thread_list.append(t) for t in thread_list: # 等待所有线程执行完毕 t.join() print('final num:', num2)
死锁:
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去
import threading, time lockA = threading.Lock() lockB = threading.Lock() class myThread(threading.Thread): def doA(self): lockA.acquire() print(self.name, "gotlockA", time.ctime()) time.sleep(3) lockB.acquire() print(self.name, "gotlockB", time.ctime()) lockB.release() lockA.release() def doB(self): lockB.acquire() print(self.name, "gotlockB", time.ctime()) time.sleep(2) lockA.acquire() print(self.name, "gotlockA", time.ctime()) lockA.release() lockB.release() def run(self): self.doA() self.doB() if __name__ == "__main__": threads = [] for i in range(5): threads.append(myThread()) for t in threads: t.start() for t in threads: t.join() print("主线程结束")
递归锁
递归锁,在Python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。
这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
class MyThread(threading.Thread): def doA(self): r_lock.acquire() print(self.name, "gotlockA", time.ctime()) time.sleep(3) r_lock.acquire() print(self.name, "gotlockB", time.ctime()) r_lock.release() r_lock.release() def doB(self): r_lock.acquire() print(self.name, "gotlockB", time.ctime()) time.sleep(2) r_lock.acquire() print(self.name, "gotlockA", time.ctime()) r_lock.release() r_lock.release() def run(self): self.doA() self.doB() if __name__ == "__main__": threads = [] for i in range(5): threads.append(MyThread()) for t in threads: t.start() for t in threads: t.join() print("主线程结束")
同步对象(Event)
#!/usr/bin/env python # -*- coding: utf-8 -*- """ 线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。 为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。 在初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象,而这Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。 一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行 event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态,等待操作系统调度; event.clear():恢复event的状态值为False。 """ import threading, time event = threading.Event() class Boss(threading.Thread): def run(self): print("BOSS:今晚大家都要加班到22:00。") print(event.isSet()) event.set() time.sleep(5) print("BOSS:<22:00>可以下班了。") print(event.isSet()) event.set() class Worker(threading.Thread): def run(self): event.wait() print("Worker:哎……命苦啊!") time.sleep(1) event.clear() event.wait() print("Worker:OhYeah!") if __name__ == "__main__": threads = [] for i in range(5): threads.append(Worker()) threads.append(Boss()) for t in threads: t.start() for t in threads: t.join()
信号量
信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数器,每当调用acquire()时-1,调用release()时+1。
计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)
BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。
from threading import Thread, Semaphore import threading import time def func(): sm.acquire() print('%s get sm' % threading.current_thread().getName()) time.sleep(3) sm.release() if __name__ == '__main__': sm = Semaphore(5) # 同一时间开5个 for i in range(23): t = Thread(target=func) t.start()
线程队列
Python Queue模块有三种队列及构造函数:
1、Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize)
2、LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize)
import queue # 线程队列 q = queue.Queue() # 创建一个队列对象,先进先出(FIFO) q.put(10) # 将一个值放入队列中 q.put("hello") q.put({"name": "lee"}) q.get() # 从队列中删除并返回一个值。
import queue q = queue.LifoQueue() q.put(10) # 将一个值放入队列中 q.put("hello") q.put({"name": "lee"}) # q.get() # 从队列中删除并返回一个值。 while True: data = q.get(block=False) # block=False, 队列若为空里,则会抛出queue.Empty异常 print(data) print("------------------")
import queue q = queue.PriorityQueue() # put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高 q.put((20, 'a')) q.put((10, 'b')) q.put((30, 'c')) print(q.get()) print(q.get()) print(q.get())
queue.Queue(maxsize=0) py2为Queue.Queue(maxsize=0) maxsize设定队列长度。如果maxsize小于等于0就表示队列长度无限,默认为0。 put(self, item, block=True, timeout=None) 如果可选args'block'为true且'timeout'为None(默认值),则在必要时阻塞,直到有空闲插入项可用。 如果'timeout'是非负数,它最多会阻止'超时'秒,如果在该时间内没有空闲插入项,则会引发Full异常。 否则('block'为false),如果空闲插入项立即可用,则将项目放在队列中,否则引发Full异常(在这种情况下忽略'timeout')。 get(self, block=True, timeout=None): 从队列中删除并返回一个项目。 如果可选args'block'为true且'timeout'为None(默认值),则必要时阻止,直到某个项可用为止。 如果'timeout'是一个非负数,它最多会阻止'超时'秒,如果在那段时间内没有项目可用,则会引发Empty异常。否则('block'为false),如果一个项目立即可用,则返回一个项目 ,否则引发Empty异常(在这种情况下忽略'timeout')。 此包中的常用方法(q = Queue.Queue()): q.qsize() 返回队列的大小 q.empty() 如果队列为空,返回True,反之False q.full() 如果队列满了,返回True,反之False q.full 与 maxsize 大小对应 q.get([block[, timeout]]) 获取队列,timeout等待时间 q.get_nowait() 从队列中删除并返回一个项目而不会阻塞。 只有一个项目立即可用才能获得。 否则引发Empty异常。 非阻塞 q.put(item) 写入队列,timeout等待时间 q.put_nowait(item) 相当q.put(item, False) q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号 q.join() 实际上意味着等到队列为空,再执行别的操作
生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
import time, random import queue, threading q = queue.Queue() def Producer(name): count = 0 while count < 10: print("making........") time.sleep(random.randrange(3)) q.put(count) print('Producer %s has produced %s baozi..' % (name, count)) count += 1 print("ok......") def Consumer(name): count = 0 while count < 10: time.sleep(random.randrange(4)) if not q.empty(): data = q.get() print(data) print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' % (name, data)) else: print("-----no baozi anymore----") count += 1 p1 = threading.Thread(target=Producer, args=('A厨师',)) c1 = threading.Thread(target=Consumer, args=('B顾客',)) p1.start() c1.start()
import time, random import queue, threading q = queue.Queue() def Producer(name): count = 0 while count < 10: print("making........") time.sleep(random.randrange(3)) q.put(count) print('Producer %s has produced %s baozi..' % (name, count)) count += 1 q.task_done() print("ok......") def Consumer(name): count = 0 while count < 3: time.sleep(random.randrange(4)) data = q.get() q.join() print(data) print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' % (name, data)) count += 1 p1 = threading.Thread(target=Producer, args=('A厨师',)) c1 = threading.Thread(target=Consumer, args=('B顾客',)) c2 = threading.Thread(target=Consumer, args=('C顾客',)) c3 = threading.Thread(target=Consumer, args=('D顾客',)) p1.start() c1.start() c2.start() c3.start()
线程池