一、threading模块
1.threading类的两种创建方法
(1)直接创建
import time
import threading
def Hi(num):
print("hello,%d"%num)
time.sleep(3)
if __name__ == "__main__":
t1 = threading.Thread(target=Hi,args=(10,))
t2 = threading.Thread(target=Hi,args=(9,))
t1.start()
t2.start()
print("ending")
(2)通过继承的方式创建
import threading,time
class MyThread(threading.Thread):
def __init__(self,num):
threading.Thread.__init__(self)
self.num = num
def run(self):
print("运行线程%s"%self.num)
time.sleep(3)
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
print("ending")
2.thread类的实例方法
(1)join()方法
在子线程完成运行之前,这个子线程的父线程将一直被阻塞。
import threading,time def music(): print("start to listen music %s"%time.ctime()) time.sleep(3) print("stop to listen music %s"%time.ctime()) def game(): print("start to play game %s"%time.ctime()) time.sleep(5) print("stop to play game %s"%time.ctime()) if __name__ == "__main__": t1 = threading.Thread(target=music) t2 = threading.Thread(target=game) t1.start() t1.join() t2.start() t2.join() print("ending")
(2)setDaemen
将线程声明为守护线程,必须在start() 方法调用之前设置,
在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程 就分兵两路,分别运行,那么当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出,如果想要
主线程完成了,不管子线程是否完成,都要和主线程一起退出,就需要设置setDaemon()
import threading,time
def music():
print("start to listen music %s"%time.ctime())
time.sleep(3)
print("stop to listen music %s"%time.ctime())
def game():
print("start to play game %s"%time.ctime())
time.sleep(5)
print("stop to play game %s"%time.ctime())
t1 = threading.Thread(target=music)
t2 = threading.Thread(target=game)
if __name__ == "__main__":
t2.setDaemon(True)
t1.start()
t1.join()
t2.start()
# t2.join()
print("ending")
(3)其他方法
import threading,time
def music():
print("start to listen music %s"%time.ctime())
time.sleep(3)
print("stop to listen music %s"%time.ctime())
def game():
print("start to play game %s"%time.ctime())
time.sleep(5)
print("stop to play game %s"%time.ctime())
t1 = threading.Thread(target=music)
t2 = threading.Thread(target=game)
if __name__ == "__main__":
t1.start()
#Thread实例对象的方法
print(t1.isAlive())#返回线程是否活动的
t1.setName("t1111")#设置线程名
print(t1.getName())#返回线程名
t1.join()
t2.start()
#threading模块提供的一些方法:
print(threading.currentThread())#返回当前的线程变量
print(threading.enumerate())#返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程
print(threading.active_count())#返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
print("ending")
3.GIL(全局解释器锁)
(1)什么是GIL
在一个线程拥有了解释器的访问权之后,其他的所有线程都必须等待它释放解释器的访问权,即使这些线程的下一条指令并不会互相影响。
GIL缺点:多处理器退化为单处理器;优点:避免大量的加锁解锁操作
因为GIL的存在,只有IO Bound场景下得多线程会得到较好的性能
(2)GIL的影响
无论启多少个线程,有多少个cpu, Python在执行一个进程的时候同一时刻只允许一个线程运行。所以,python是无法利用多核CPU实现多线程的。
这样,python对于计算密集型的任务开多线程的效率甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。
(3)解决方案
用multiprocessing,它完整的复制了一套thread所提供的接口方便迁移,它使用了多进程而不是多线程,每个进程有自己的独立的GIL,因此不会出现进程之间的GIL争抢。
import multiprocessing
import time
def run(a1):
time.sleep(3)
print(a1)
if __name__ == '__main__':
t1 = multiprocessing.Process(target=run, args=(12,))
t1.daemon = True # 将daemon设置为True,则主线程不比等待子进程,主线程结束则所有结束
t1.start()
t2 = multiprocessing.Process(target=run, args=(13,))
t2.daemon = True
t2.start()
print('end') # 默认情况下等待所有子进程结束,主进程才结束
4.同步锁
import time
def sub():
global num
lock.acquire()
temp = num
time.sleep(0.001)
num = temp -1
lock.release()
num = 100
lock = threading.Lock()
l = []
for i in range(100):
t = threading.Thread(target = sub)
t.start()
l.append(t)
for t in l:
t.join()
print(num)
5.死锁和递归锁
(1)死锁
死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
import threading,time
class MyThread(threading.Thread):
def actionA(self):
A.acquire()
print(self.name,"gotA",time.ctime())
time.sleep(2)
B.acquire()
print(self.name,"gotB",time.ctime())
time.sleep(1)
B.release()
A.release()
def actionB(self):
B.acquire()
print(self.name, "gotB", time.ctime())
time.sleep(2)
A.acquire()
print(self.name, "gotA", time.ctime())
time.sleep(1)
A.release()
B.release()
def run(self):
self.actionA()
self.actionB()
if __name__ == "__main__":
A = threading.Lock()
B = threading.Lock()
l = []
for i in range(5):
t = MyThread()
t.start()
l.append(t)
for i in l:
i.join()
print("ending....")
(2)递归锁
可重入锁RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。
import threading,time
class MyThread(threading.Thread):
def actionA(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 actionB(self):
r_lock.acquire()
print(self.name, "gotB", time.ctime())
time.sleep(2)
r_lock.acquire()
print(self.name, "gotA", time.ctime())
time.sleep(1)
r_lock.release()
r_lock.release()
def run(self):
self.actionA()
self.actionB()
if __name__ == "__main__":
r_lock = threading.RLock()
l = []
for i in range(5):
t = MyThread()
t.start()
l.append(t)
for i in l:
i.join()
print("ending....")
6.event对象
(1)方法
event.isSet():返回event的状态值;
event.wait():如果 event.isSet()==False将阻塞线程;
event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
event.clear():恢复event的状态值为False。
(2)用法
在初始情况下,Event对象中的信号标志被设置为假。如果有线程等待的一个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。
wait方法可接受一个超时参数,加入这个超时参数之后,如果阻塞时间超过这个参数设定的值之后,wait方法会返回。
(3)实例
import threading,time
class Teacher(threading.Thread):
def run(self):
print("teacher:拖堂两分钟哈")
print(event.isSet())
event.set()
time.sleep(5)
print("teacher下课!!!")
print(event.isSet())
event.set()
class Students(threading.Thread):
def run(self):
event.wait()
print("students:啊啊啊啊啊!")
time.sleep(3)
event.clear()
event.wait()
print("students:又上课了!!!!!")
if __name__ == "__main__":
event = threading.Event()
threads = []
for i in range(5):
threads.append(Students())
threads.append(Teacher())
for t in threads:
t.start()
for t in threads:
t.join()
print("ending")
7.信号量(semaphore)
(1)用法
Semaphore管理一个内置的计数器,每当调用acquire()时内置计数器-1;调用release() 时内置计数器+1;计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
(2)实例
import threading,time
semaphore = threading.Semaphore(5)
def func():
if semaphore.acquire():
print(threading.currentThread().getName())
time.sleep(2)
semaphore.release()
if __name__ == "__main__":
for i in range(20):
t1 = threading.Thread(target=func)
t1.start()
'''
import threading,time
class MyThread(threading.Thread):
def run(self):
if semaphore.acquire():
print(self.name)
time.sleep(3)
semaphore.release()
if __name__ == '__main__':
semaphore = threading.Semaphore(5)
thrs = []
for i in range(20):
thrs.append(MyThread())
for t in thrs:
t.start()
8.队列(queue)
(1)方法
import Queue
q = queue.Queue(maxsize = 10)
# 创建一个“队列”对象
# queue.Queue类即是一个队列的同步实现。队列长度可为无限或者有限。
# 可通过Queue的构造函数的可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限。
q.put(10)
# 将一个值放入队列中
# 调用队列对象的put()方法在队尾插入一个项目。
# put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。
# 如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。
# 如果block为0,put方法将引发Full异常。
q.get()
# 将一个值从队列中取出
# 调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。
# 如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。
# 如果队列为空且block为False,队列将引发Empty异常。
(2)其他方法
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() #实际上意味着等到队列为空,再执行别的操作
(3)实例
a.先进先出
import queue
q = queue.Queue()
q.put(1)
q.put("he")
q.put({"name":"chouzi"})
while True:
data = q.get()
print(data)
print("$$$$$$$$$$$$$$$$$")
b.先进后出
import queue
q = queue.LifoQueue()
q.put(1)
q.put("he")
q.put({"name":"chouzi"})
while True:
data = q.get()
print(data)
print("$$$$$$$$$$$$$$$$$")
c.指定优先级
import queue
q = queue.PriorityQueue()
q.put([2,1])
q.put([1,"he"])
q.put([3,{"name":"chouzi"}])
while True:
data = q.get()
print(data[1])
print(data[0])
print("$$$$$$$$$$$$$$$$$")
d.task_done()
import queue
q = queue.Queue(5)
q.put(10)
q.put(20)
print(q.get())
q.task_done()
print(q.get())
q.task_done()
q.join()
print("ending!")
e.其他方法
import queue
q = queue.Queue(4)#FIFO
q.put(1)
q.put("he")
q.put({"name":"chouzi"})
q.put_nowait("hh")#put(block = False) get_nowait与其类似
print(q.qsize())
print(q.empty())
print(q.full())
9.生产者消费者模型
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者通过阻塞队列来进行通讯,所以生产者生产完数据之后直接扔给阻塞队列,消费者直接从阻塞队列里取。阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
import time,threading,queue
q = queue.Queue()
def Producer(name):
count = 0
while count< 10 :
print("making GSCM")
time.sleep(5)
q.put(count)
print("%s 把做好了GSCM%s" %(name,count))
count += 1
q.task_done()
print("吃吧!!!!")
def Consumer(name):
count = 0
while count < 10 :
time.sleep(6)
q.join()
data = q.get()
print("%s 把 GSCM%s吃了。"%(name,count))
p1 = threading.Thread(target=Producer,args=("hechouzi",))
c1 = threading.Thread(target=Consumer,args = ("nuannuan",))
c2 = threading.Thread(target=Consumer,args=("hanna",))
c3 = threading.Thread(target=Consumer,args=("wujiao",))
p1.start()
c1.start()
c2.start()
c3.start()