并发&并行
并发:是指系统具有处理多个任务(动作)的能力。
并行:是指系统具有 同时 处理多个任务(动作)的能力。
同步&异步
同步:当进程执行到一个IO(等待外部数据)的时候,-----等:同步
异步: -----不等:一直等到数据接受成功,再回来处理
问题: 多核没利用上?
GIL:全局解释锁
因为有GIL,所以,同一时刻,只有一个线程被CPU执行
任务:IO密集型 & 计算密集型
对于IO密集型的任务 :python的多线程的是有意义的
可以采用多进程+协程
对于计算密集型的任务:python的多线程就不推荐,python就不适用了
线程与进程的区别:
1 一个程序至少有一个进程,一个进程至少有一个线程.(进程可以理解成线程的容器) 2 进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 3 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和 程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 4 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调 度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程 自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈)但是 它可与同属一个进程的其他的线程共享进程所拥有的全部资源. 一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
无论你启多少个线程,你有多少个cpu, Python在执行的时候会淡定的在同一时刻只允许一个线程运行
线程
import threading import time def sayhi(num): #定义每个线程要运行的函数 print("running on number:%s" %num) time.sleep(3) if __name__ == '__main__': t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例 t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例 t1.start() #启动线程 t2.start() #启动另一个线程 print(t1.getName()) #获取线程名 print(t2.getName())
import threading import time class MyThread(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self):#定义每个线程要运行的函数 print("running on number:%s" %self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start() print("ending......")
其他方法:
# run(): 线程被cpu调度后自动执行线程对象的run方法 # start():启动线程活动。 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
例:
import threading from time import ctime,sleep import time def music(func): print("Begin listening to %s. %s"%(func,ctime())) sleep(4) print("end listening %s"%ctime()) def move(func): print("Begin watching at the %s!%s"%(func,ctime())) sleep(5) print("end watching %s"%ctime()) threads=[]#用来放线程的对象 t1=threading.Thread(target=music,args=("七里香",)) threads.append(t1) t2=threading.Thread(target=move,args=("啊甘正传",)) threads.append(t2) if __name__ == '__main__': t1.setDaemon(True)#守护线程,主进程关闭则t1关闭。 t2.setDaemon(True)#主线程关闭时则关闭 for t in threads:#循环得到线程的实例 t.start()#开启线程 # print(threading.currentThread())#打印线程的对象 # print(threading.enumerate())#打印正在开启的线程列表 # print(threading.activeCount())#打印正在开启的线程个数 # print(t2.isAlive())#线程是否运行 # t2.setName(123)#跟改线程名 # print(t.getName())#查看线程名 # t1.join()#等待线程结束,在往下走 # t2.join() print("all over %s"%ctime())
同步锁(也叫互斥锁):
import time import threading def sub(): global num temp=num time.sleep(0.00001) num=temp-1 num=100 l=[] for i in range(100): t=threading.Thread(target=sub) t.start() l.append(t) for i in l:i.join() print(num)
lock=threading.Lock(),加上同步锁。
import time import threading def sub(): global num lock.acquire() temp=num time.sleep(0.00001) num=temp-1 lock.release() num=100 l=[] lock=threading.Lock() for i in range(100): t=threading.Thread(target=sub) t.start() l.append(t) for i in l:i.join() print(num)
死锁和递归锁:
在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁,因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。下面是一个死锁的例子:
import threading import time class MyThresd(threading.Thread):#类的继承方式启动线程 def actionA(self): A.acquire() #获得A锁 print(self.name,"gotA",time.ctime()) time.sleep(2) B.acquire() #获得B锁 print(self.name,"gotB",time.ctime()) time.sleep(1) B.release() #释放B锁 A.release() #释放A锁 def actionB(self): B.acquire() print(self.name,"gotA",time.ctime()) time.sleep(2) A.acquire() print(self.name,"gotB",time.ctime()) time.sleep(1) A.release() B.release() def run(self): self.actionA() self.actionB() if __name__ == '__main__': A=threading.Lock() #产出A锁 B=threading.Lock() #产生B锁 # r_lock=threading.RLock() #递归锁,把A、B锁换成Rlock() l=[] for i in range(5): t=MyThresd() #实例创建线程 t.start() #启动线程,执行run方法 l.append(t) #对象加到列表里 for i in l: #便利获取对象 i.join() #等待线程结束 print("ending.....")
为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
import threading import time class MyThresd(threading.Thread):#类的继承方式启动线程 def actionA(self): r_lock.acquire() #获得A锁 print(self.name,"gotA",time.ctime()) time.sleep(2) r_lock.acquire() #获得B锁 print(self.name,"gotB",time.ctime()) time.sleep(1) r_lock.release() #释放B锁 r_lock.release() #释放A锁 def actionB(self): r_lock.acquire() print(self.name,"gotA",time.ctime()) time.sleep(2) r_lock.acquire() print(self.name,"gotB",time.ctime()) time.sleep(1) r_lock.release() r_lock.release() def run(self): self.actionA() self.actionB() if __name__ == '__main__': # A=threading.Lock() #产出A锁 # B=threading.Lock() #产生B锁 r_lock=threading.RLock() #递归锁,把A、B锁换成Rlock() l=[] for i in range(5): t=MyThresd() #实例创建线程 t.start() #启动线程,执行run方法 l.append(t) #对象加到列表里 for i in l: #便利获取对象 i.join() #等待线程结束 print("ending.....")
解死锁:acquire有一个计数,获得一把锁就加一,只要计数器一直大于0,其他线程就要一直等待
同步条件(Event):
event = threading.Event()
event.wait() #等待event被设定
event.set() #设定event
event.clear() #清空event的设定
import threading,time class Boss(threading.Thread): def run(self): print("Boss:今晚大家都要加班到22;00") print(event.isSet()) #打印是否被设定默认是False event.set() #设定标志位 time.sleep(4) print("22:00到了,下班吧!") event.set() class Worker(threading.Thread): def run(self): event.wait() #阻塞等待,等待标志位被设定,设定就往下走 print("太苦B了") # event.clear() #清空标志位 time.sleep(2) event.wait() #清空event后,再次阻塞 print("终于下班了") if __name__ == '__main__': event=threading.Event() #相当于是个标志位 threads=[] for i in range(5): threads.append(Worker()) #创建5个线程对象 threads.append(Boss()) #创建1个线程对象 for t in threads: t.start() #所有线程全部执行 for t in threads: t.join() #等待线程全部执行完 print("ending.....")
信号量(Semaphore):
import threading,time class MyThread(threading.Thread): def run(self): if semaphore.acquire(): #一次可以进来5把锁 print(self.name) time.sleep(2) semaphore.release() if __name__ == '__main__': semaphore=threading.Semaphore(5) #获取锁的对象,设置同时开启5个线程,默认是一个 threads=[] for i in range(100): threads.append(MyThread()) for t in threads: t.start() for t in threads: t.join()
*多线程利器---队列(queue)
列表是不安全的数据结构
import threading,time,queue li=[1,2,5,3,4,5] print(li) def pri(): while li : a=li[-1] print(a) time.sleep(1) # try: li.remove(a) # except Exception as e: # print("-"*10,a,e) t1=threading.Thread(target=pri) t1.start() t2=threading.Thread(target=pri) t2.start()
queue列队类的方法:
创建一个“队列”对象 import Queue q = Queue.Queue(maxsize = 10) #参数代表列队能放多少个值 Queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。 将一个值放入队列中 q.put(10) #block默认为True,改为False则抛出异常 调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为 1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。 将一个值从队列中取出 q.get() #block默认为True,改为Flase则抛出异常 调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True, get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。 Python Queue模块有三种队列及构造函数: *1、Python Queue模块的FIFO队列先进先出。 class queue.Queue(maxsize) *2、LIFO类似于堆,即先进后出。 class queue.LifoQueue(maxsize) *3、还有一种是优先级队列级别越低越先出来。 class queue.PriorityQueue(maxsize) 此包中的常用方法(q = Queue.Queue()): q.qsize() 返回队列的大小 q.empty() 如果队列为空,返回True,反之False q.full() 如果队列满了,返回True,反之False q.full 与 maxsize 大小对应 q.get([block[, timeout]]) 获取队列,timeout等待时间 q.get_nowait() 相当q.get(False) 非阻塞 q.put(item) 写入队列,timeout等待时间 q.put_nowait(item) 相当q.put(item, False) q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号 q.join() 实际上意味着等到队列为空,再执行别的操作
其他模式:
import queue #先进后出 q=queue.LifoQueue() q.put(34) q.put(56) q.put(12) #优先级 # q=queue.PriorityQueue() # q.put([5,100]) # q.put([7,200]) # q.put([3,"hello"]) # q.put([4,{"name":"alex"}]) while 1: data=q.get() print(data)
生产者消费者模型:
为什么要使用生产者和消费者模式
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。
什么是生产者消费者模式
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
这就像,在餐厅,厨师做好菜,不需要直接和客户交流,而是交给前台,而客户去饭菜也不需要不找厨师,直接去前台领取即可,这也是一个结耦的过程。
import time,random import queue,threading q=queue.Queue() def Producer(name): count=0 while count < 10: print("making......") time.sleep(2) q.put(count) print("Producer %s has produced %s baozi.."%(name,count)) count+=1 # q.task_done() #发信号,放了一个数据 q.join() print("ok......") def Consumer(name): count=0 while True: time.sleep(4) data = q.get() # 接收列队的数据 print("eating.....") # q.join() #队列有数据了才往下走 q.task_done() print("