进程与线程
操作系统定义:操作系统是一个用来协调,管理,和控制计算机硬件和软件的系统程序,位于硬件和软件之间
多道程序设计,如下:
如果A遇到IO阻塞,就进行B,如果B遇到 IO阻塞,就进行C ,C运行完再运行BA
A-----IO阻塞
B--------IO阻塞
C------
进程
进程的定义:
进程本质就是一段程序的运行过程(抽象概念)
进程就是一个程序在一个数据集上的一次动态执行过程,是最小的资源单位
进程一般由程序,数据集,进程控制块三部分组成
程序:我们编写的程序用来描述进程要完成的哪些功能以及如何完成
数据集:程序在执行过程中所需要使用的资源
进程控制块:用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
进程是为了实现切换,并发。
多进程模块 multiprocessing
由于GIL的存在,python 中的多线程其实并不是真正的多线程,如果想要充分的使用多核CPU的资源,在python中大部分情况需要使用多进程,所有子进程运行完比,主进程才会结束退出
创建多进程
import multiprocessing 引入模块
进程名= multiprocessing.Process(target=函数名,args=(,)) 如果函数需要传入参数,则需要写args,否则不写。
调用多进程方式一:
import multiprocessing
import time
def f(name):
time.sleep(1)
print('hello', name,time.ctime())
if __name__ == '__main__':
p_list=[]
for i in range(3):
p = multiprocessing.Process(target=f, args=('alvin',))
p_list.append(p)
p.start()
for i in p_list:
i.join()
print('end')
结果是:以下结果一起出现(同多线程运行一样)
hello MyProcess-1 Sun Jul 11 11:12:32 2021
hello MyProcess-2 Sun Jul 11 11:12:32 2021
hello MyProcess-3 Sun Jul 11 11:12:32 2021
end
调用多进程方式二:类方法调用
import multiprocessing
import time
class MyProcess(multiprocessing.Process):
def __init__(self):
super(MyProcess, self).__init__()
self.name = name
def run(self):
time.sleep(1)
print ('hello', self.name,time.ctime())
if __name__ == '__main__':
p_list=[]
for i in range(3):
p = MyProcess()
p.start()
p_list.append(p)
for p in p_list:
p.join()
print('end')
结果是:一下午内容一起打印:
hello MyProcess-1 Sun Jul 11 11:51:17 2021
hello MyProcess-2 Sun Jul 11 11:51:17 2021
hello MyProcess-3 Sun Jul 11 11:51:17 2021
end
进程的实例方法:
is_alive():进程是否在运行
join([time out]):阻塞当前上下文环境的进程,知道调用此方法的进程终止或者到达指定的timeout(可选参数)。
start(): 进程准备就绪,等待CPU调度
run(): start 调用run 方法,如果实例进程未指定传入的target,start执行默认run 方法
terminate():不管任务是否完成,立即停止工作进程。
进程的属性:
-保护进程(daemon)
保护进程:只要主进程结束,保护的进程不论是否执行完毕都会随主进程结束 与线程的setDeamon 功能一样
代码:进程名.daemon = True
保护进程需要放在start 之前
import multiprocessing
import time
class MyProcess(multiprocessing.Process):
# def __init__(self):
# super(MyProcess, self).__init__()
# #self.name = name
def run(self):
time.sleep(1)
print ('hello', self.name,time.ctime())
if __name__ == '__main__':
p_list=[]
for i in range(3):
p = MyProcess()
p.daemon=True
p.start()
p_list.append(p)
print('end')
结果是:只会打印以下内容:
end
-name 进程名字
-pid 进程号
进程间的通讯的三种方式
1,队列
import multiprocessing
import time
def foo(s):
time.sleep(1)
print("son process",id(q))
q.put(123)
q.put("yuan")
if __name__ == "__main__":
q = multiprocessing.Queue() #创建主进程队列
p = multiprocessing.Process(target=foo,args =(q,))
#此处创建了子进程,并将主进程队列传给了子进程,这样子进程可以获得队列q
p.start()
p.join()
print("main process", id(q))
print(q.get())
print(q.get())
结果是:
先出现:
son process 140366595030712
main process 140366595030712
后出现:
123
yuan
2,管道Pipe
from multiprocessing import Process,Pipe
import time
def f(conn):
conn.send([12,{'name':'yuan'},'hello'])
response=conn.recv()
print('response',response)
conn.close()
print("q_ID2:", id(conn))
if __name__ == "__main__":
parent_conn, child_conn = Pipe() # 双向管道
print("q_ID1:", id(child_conn))
p = Process(target=f, args=(child_conn,))#将管道传给子进程p
p.start()
print(parent_conn.recv())
parent_conn.send("儿子你好!")
p.join()
结果是:一起打印以下内容:
q_ID1: 140541750354272
[12, {'name': 'yuan'}, 'hello']
response 儿子你好!
q_ID2: 140541750354272
3, Manager :只有Manager会数据共享
from multiprocessing import Process,Manager
import time
def f(d,l,n):
d[n]= '1'
l.append(n)
if __name__ == "__main__":
with Manager() as manager:
d = manager.dict()#创建空字典
l = manager.list(range(5))#创建表格[0,1,2,3,4]
p_list = []
for i in range(4):
p = Process(target=f, args=(d, l, i)) #创建4个子进程
p.start()
p_list.append(p)
for res in p_list:
res.join()
print(d)
print(l) #表格原来是l= [0,1,2,3,4,] 运行完毕后变成[0,1,2,3,4,0,1,2,3]
结果是:
{0: '1', 1: '1', 2: '1', 3: '1'}
[0,1,2,3,4,0,1,2,3]
进程同步
from multiprocessing import Process,Lock
def f(l,i):
l.acquire()#获得锁
print('hello world %s'%i)
l.release()#释放锁,也是解锁
# with l,相当于上边的加同步锁整个过程,代码如下:
#with l:
# print('hello world %s'%i)
if __name__ == '__main__':
lock = Lock()
for i in range(5):
s=Process(target=f,args=(lock, i,))#创建子进程
s.start()
结果是:
hello world 0
hello world 1
hello world 2
hello world 3
hello world 4
进程池
创建进程池
from multiProcessing import Pool
进程池名 = Pool(X) #===========X代表可同时进入进程池的进程数量
注意:在进程的最后需要加上以下两行代码,这两行代码的调用顺序是固定的如下:
pool.close()
pool.join()
2种进程池:
-异步:
进程池名.apply_async(func = 函数名,args=函数所需传入的参数,callback=回调函数名)
args 需要是可迭代对象例如:(x,),如果不需要传入参数,可不写args
回调函数:就是某个动作或者函数执行成功后,再去执行的函数,需要回调则写callback,不需要可不写
from multiprocessing import Pool
import time,os
def Foo(i):
time.sleep(1)
print(i)
print('Foo', os.getpid())#此处的ID是子进程ID
return i
def Bar(args):#此处会将之前运行函数的结果传入进来,即args=i
print('Bar',os.getpid())#此处的ID号会与主进程一致
print(args)
pool = Pool(5) #创建进程池对象pool,进程池同时可进入5个进程
if __name__ == '__main__':
print('main',os.getpid())#打印主进程的ID
for i in range(20):
pool.apply_async(func=Foo,args=(i,),callback=Bar)
pool.close()
pool.join()
print('end....')
-同步
进程池名.apply(func =函数名,args=(x,)).args 同上异步,如需传入参数,可写入args,若不需要传入参数,可不写args.
from multiprocessing import Pool
import time
def Foo(i):
time.sleep(1)
print(i)
pool = Pool(5) #创建进程池对象pool,进程池同时可进入5个进程
if __name__ == '__main__':
for i in range(20):
pool.apply(func=Foo,args=(i,))
pool.close()
pool.join()
print('end....')
线程
-系统线程
1)一个程序至少有一个进程,一个进程至少有一个线程。(进程可以理解成线程的容器)
2)进程在执行的过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
3)线程在执行的过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口,顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
4)进程是具有一定独立功能的程序,关于摸个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程是进城的一个实体,是最小的执行单位,是cpu调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
一个线程可以创建和撤销另一个线程,同一个进程的多个线程之间是可以并发进行的。
作用:线程是为了降低上下文切换的消耗,提高系统的并发性。
python的线程
因为GIL全局解释锁,所以同一时刻,只能有一个线程被CPU执行
所以:无论开启多少个线程,和有多少个CPU,python 在执行时同一时刻只允许一个线程运行,
即:python 中一个进程同一时间只有一个线程能运行。如下图:在一个进程中有3个线程:add,mul,main ,他们三个同一时间只能有一个能运行,他们是相互竞争的关系,cpu会在他们之间来回的切换。
程序运行时,执行一个主线程,如果主线程又创建一个子线程,主线程就会和子线程兵分2路,分别运行,那么当主线程运行完毕会检验子线程是否执行完毕,如果子线程未完成,则主线程会等子线程执行完毕后一起退出。
线程与线程之间是相互独立的,多线程可以并发进行。
子线程全部运行完毕(除了setDaemon线程外),子线程的主线程才会退出,
创建线程的2种方法
运用threading 模块,创建一个线程
方式一:直接用threading 模块(常用)
代码如下:
import threading #x线程
变量名 = threading.Thread(target =函数名,args =可迭代对象名)#创建子线程
注意当函数名不需要输入内容时,args可以不写
变量名.start() #运行子线程
具体例子如下:
import threading # 线程
import time
def Hi(num):
print('hello %d' % num)
time.sleep(3)
# if __name__ == '__main__':
# Hi(9)
# Hi(8)
# Hi(7)
# # 以上各个结果之间会间隔3秒,最后结束
if __name__ =='__main__':
t1 = threading.Thread(target=Hi,args=(9,))#创建子线程对象t1
t1.start()#运行子线程t1
t2 = threading.Thread(target=Hi, args=(8,)) # 创建子线程对象t2
t2.start() # 运行子线程t2
t3 = threading.Thread(target=Hi, args=(7,)) # 创建子线程对象t3
t3.start() # 运行子线程对象t3
print('ending....') #此处运行的是主线程
#结果是:子线程对象t1,t2,t3以及主线程运行的结果会同时出现在屏幕上,然后共同停留3秒钟,再结束
-----------------------------
import threading
import time
def music():
print('begin to listen %s'%time.ctime())
time.sleep(5)
print('stop to listen %s'%time.ctime())
def game():
print('begin to play game %s'%time.ctime())
time.sleep(3)
print('stop to play game %s'%time.ctime())
if __name__ == '__main__':
t1 = threading.Thread(target=music)
t1.start()#===运行结果是:begin to listen Wed Jul 7 12:04:35 2021
#stop to listen Wed Jul 7 12:04:40 2021
t2 = threading.Thread(target=game)
t2.start()#===运行结果是:begin to play game Wed Jul 7 12:04:35 2021
#stop to play game Wed Jul 7 12:04:38 2021
print('主线程=====>%s'%time.ctime())
结果是:
解析:
先出现以下内容:
begin to listen Thu Jul 8 10:43:49 2021
begin to play game Thu Jul 8 10:43:49 2021
主线程=====>Thu Jul 8 10:43:49 2021
3秒后再出现:
stop to play game Thu Jul 8 10:43:52 2021
再2秒后出现:
stop to listen Thu Jul 8 10:43:54 2021
总共只用了5秒。
方式二:用类方法(继承)创建新的子线程
注意:run()方法是重新定义的,所以必须是run 不能改为其他的名字
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,time is %s' % (self.num, time.ctime()))
time.sleep(3)
if __name__ == '__main__':
t1= MyThread(1)
t2 = MyThread(2)
t1.start()
t2.start()
print('ending.....time is %s'%time.ctime())
结果是:
首先打印:
running on number :1,time is Wed Jul 7 21:24:36 2021
running on number :2,time is Wed Jul 7 21:24:36 2021
ending.....time is Wed Jul 7 21:24:36 2021
然后等待3秒,再结束
threading.Thread的实例方法:
1)join 方法在线程中的运用
join():代表:碰到运用join方法的这个线程,需要此线程执行完毕,才能再往下执行其他的代码/线程。
在子线程完成运行之前,这个子线程的主线程将一直被阻塞,无法结束,只有所有的子线程全部执行完毕,主线程才会退出。
代码:子线程名.join()
注意:join 必须在start 之后
import time
def music():
print('begin to listen %s'%time.ctime())
time.sleep(5)
print('stop to listen %s'%time.ctime())
def game():
print('begin to play game %s'%time.ctime())
time.sleep(3)
print('stop to play game %s'%time.ctime())
threads = []
t1 = threading.Thread(target=music)
t2 = threading.Thread(target=game)
threads.append(t1)
threads.append(t2)
if __name__ == '__main__':
print('主线程==%s' % time.ctime())
t1.start()
t1.join()
t2.start()
结果是:
主线程==Wed Jul 7 16:29:45 2021
begin to listen Wed Jul 7 16:29:45 2021
stop to listen Wed Jul 7 16:29:50 2021
begin to play game Wed Jul 7 16:29:50 2021
stop to play game Wed Jul 7 16:29:53 2021
解析:
首先先打印以下内容:
主线程==Wed Jul 7 16:29:45 2021
begin to listen Wed Jul 7 16:29:45 2021
然后5秒后打印:由于设置了t1.join(),所以只有t1线程执行完毕才能执行下边的代码
stop to listen Wed Jul 7 16:29:50 2021
begin to play game Wed Jul 7 16:29:50 2021
然后再3秒后打印
stop to play game Wed Jul 7 16:29:53 2021
总共用时8秒。
---------------------------
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,time is %s' % (self.num, time.ctime()))
time.sleep(3)
if __name__ == '__main__':
t1= MyThread(1)
t2 = MyThread(2)
t3 = MyThread(3)
t1.start()
t3.start()
t3.join()
t2.start()
print('ending.....time is %s'%time.ctime())
结果是:
先打印出:
running on number :1,time is Thu Jul 8 10:50:13 2021
running on number :3,time is Thu Jul 8 10:50:13 2021
然后t3线程运行完毕,打印:
running on number :2,time is Thu Jul 8 10:50:16 2021
ending.....time is Thu Jul 8 10:50:16 2021
2)setDaemon 方法
setDaemon 守护线程:守护的子线程,不论是否执行完毕都会跟父线程一起退出。
代码: setDaemon(True)
一定需要在start 之前设置 setDaemon
import threading
import time
def music():
print('begin to listen %s'%time.ctime())
time.sleep(5)
print('stop to listen %s'%time.ctime())
def game():
print('begin to play game %s'%time.ctime())
time.sleep(3)
print('stop to play game %s'%time.ctime())
threads = []
t1 = threading.Thread(target=music)
t2 = threading.Thread(target=game)
threads.append(t1)
threads.append(t2)
if __name__ == '__main__':
t1.setDaemon(True)#设置t1是守护线程
for t in threads:
t.start()
print('==%s'%time.ctime())
结果是:
begin to listen Wed Jul 7 16:00:19 2021
begin to play game Wed Jul 7 16:00:19 2021
==Wed Jul 7 16:00:19 2021
stop to play game Wed Jul 7 16:00:22 2021
解析:设置t1是守护线程。
开始运行,首先会打印如下内容:
begin to listen Wed Jul 7 16:00:19 2021
begin to play game Wed Jul 7 16:00:19 2021
==Wed Jul 7 16:00:19 2021
然后3秒后,会打印:
stop to play game Wed Jul 7 16:00:22 2021
t2子线程执行完毕,父线程会退出,由于t1是守护线程,t1会根父线程一起退出。所以t1线程对象中5秒后再执行的代码不会执行。
所以总共用时3秒
**********************************************
如果将t1,t2都设置成守护线程,结果是:
if __name__ == '__main__':
t1.setDaemon(True)
t2.setDaemon(True)
t1.start()
t2.start()
print('==%s'%time.ctime())
结果是:只会一起打印一下内容,打印完毕立刻结束
begin to listen Thu Jul 8 11:03:03 2021
begin to play game Thu Jul 8 11:03:03 2021
==Thu Jul 8 11:03:03 2021
3)run() 用以表示线程活动的方法
4)start() 启动线程
5)isAlive() 判断线程是否是活动的,如果是活动的,则返回True,否则返回False
6)getName() 线程名
7)setName() 设置线程名
例如:
上边的代码和上个例子一样。
if __name__ == '__main__':
print(t1.getName()) #查看线程名字 ===结果是:Thread-1
t1.setName('s')#设置线程名字
print(t1.getName())#===结果是:s
print(t1.isAlive())#查看线程是否活动===结果是:False
threading模块提供的方法
1)threading.currentThread() 返回当前的线程变量(主线程)
2)threading.enumerate() 返回一个包含正在运行的线程的list .
正在运行线程指的是线程启动后、结束前,不包括启动前和终止后的线程
3)threading.activeCount() :正在运行的线程的数量,与len(threading.enumerate())有相同结果
如下:
import threading
import time
def music():
print('begin to listen %s'%time.ctime())
time.sleep(5)
print('stop to listen %s'%time.ctime())
def game():
print('begin to play game %s'%time.ctime())
time.sleep(3)
print('stop to play game %s'%time.ctime())
threads = []
t1 = threading.Thread(target=music)
t2 = threading.Thread(target=game)
threads.append(t1)
threads.append(t2)
if __name__ == '__main__':
t1.start()
print('>>>>', threading.currentThread())
#查看当前的主线程
#结果是:<_MainThread(MainThread, started 4395449856)>
print('----account',threading.activeCount())
#查看有几个线程在活动
# ===>结果是----account 2 一个是主线程一个是子线程t1
print('--活动线程',threading.enumerate())
#查看活动线程列表
#--活动线程 [<_MainThread(MainThread, started 4696043008)>, <Thread(Thread-1, started 123145374748672)>]
线程同步锁(Lock)
并发:是指系统具有处理多个任务的能力
并行:是指系统具有同时处理多个任务的能力,并行是并发的子集
同步:当进程执行到一个IO(等待外部数据)的时候,-----等待: 是同步
异步:当进程执行到一个IO(等待外部数据)的时候,-----不等待: 一直等到数据接收成功,再回来处理是 异步
任务: IO密集型(例如:accept,recevice,sleep等) ,计算密集型
IO 密集型:python 的多线程是有意义的
可采用多进程+协程
计算密集型:python就不适用了。
同步锁:被锁的内容,cpu只能执行一个线程,不能随意切换。
import threading
import time
def sub():
global num
num -=1
num = 100
l=[]
for i in range(100):
t = threading.Thread(target=sub)
t.start()
l.append(t)
for s in l:
t.join()
print(num)#=====结果是0
#因为n-=1 操作指令太短,操作指令的时间小于线程之间轮循的时间,变量num会及时更新,新的线程拿到的num是更新后的。
**************************
若加上:temp = num
time.sleep(0.01)
num = temp -1
以上代码相当于 num -=1
import threadingimport time
def sub():
global num
temp = num
time.sleep(0.01)
num = temp-1
num = 100
l=[]
for i in range(100):
t = threading.Thread(target=sub)
t.start()
l.append(t)
for s in l:
t.join()
print(num)#===结果是98
#因为第一个线程拿到num 后,需要先给temp 赋值,才能更新num,这段时间大于线程之间的轮循时间
,所以其他的99个线程拿到的num都是100 ,只有一个线程拿到的num 是99,所以结果是98
解决上述数据安全问题,用同步锁:
代码如下:
同步锁名=threading.Lock() #=====创建同步锁:
同步锁名.acquire() #====获得锁,从此处开始锁
......... #中间的这些代码都是被锁的,cpu只能执行一个线程不能切换,直到这些代码执行完毕
同步锁名.release() #====释放锁,解锁
例如:
import threading
import time
def sub():
global num
lock.acquire() # 获得同步锁,从此处开始锁
temp = num
time.sleep(0.01)
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 s in l:
t.join()
print(num)#===》结果是0
线程递归锁(RLock)
递归锁:只要一个线程获得了递归锁,其他的线程都必须等待这个线程执行完被锁的代码,释放递归锁后。其他的线程才能竞争获得锁
代码:
递归锁名.RLock() #====>获得递归锁,开始锁
....... #====》中间的这些代码都是被锁的,cpu只能执行一个线程不能切换,直到这些代码执行完毕
递归锁名.release() #=====>释放递归锁,解锁
递归锁的原理:
只要一个线程获得了递归锁,其他的线程就不能获得递归锁
递归锁的原理是计数。递归锁的原始状态计数是0,当线程获得一次递归锁计数+1,只要计数>0,其他的线程都不能获得它
如下:
import threading
import 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__":
l = []
r_lock = threading.RLock()#创建递归锁
for i in range(5):
t = MyThread()
t.start()
l.append(t)
for s in l:
s.join()
print('ending......')
线程:同步条件对象(Event)
线程与线程之间都是独立的自己运行自己的,但是可以设置在某种条件下线程同步(同步条件对象),否则还是自己运行自己的
同步条件对象创建:代码:同步条件对象名 = threading.Event()
同步条件对象的方法
1) 同步条件对象名.set() #=====>设置同步条件,相当于代码pass
*2) 同步条件对象名.wait() #======> 如果同步条件设置了(即有代码:同步条件对象名.set()),代码会继续运行,
#=======>否则,线程会一直在此处阻塞,无法向下运行,直到设置了同步条件
3)同步条件对象名.clear() #=======>清除之前的同步条件设定
4)同步条件对象名.isSet() #========>查看是否设置了同步条件
import threading,time
class Boss(threading.Thread):
def run(self):
print("BOSS:今晚大家都要加班到22:00。")
print(event.isSet())# False 查看是否设置同步条件对象,设置返回True,否则返回False
event.set()# 设置同步条件,等同于pass
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__":
event=threading.Event()#====>创建了同步条件对象event
threads=[]
for i in range(5):
threads.append(Worker()) #====》创建了5线程 Work
threads.append(Boss())#===>创建了1个线程 BOSS
for t in threads:
t.start()
for t in threads:
t.join()
print("ending.....")
解析:一旦开始运行,5个Worker 线程由于同步条件对象未设置,会在event.wait() 处阻塞。
而BOSS线程会先打印:BOSS:今晚大家都要加班到22:00,然后查看是否设置同步条件对象,结果打印False.然后设置同步条件对象。
BOSS线程刚一设置同步条件对象,5个Worker线程立刻捕捉到,会立刻同时执行,同时每个现场都会各打印:Worker:哎……命苦啊!然后执行event.clear() 清除同步条件对象,到event.wait()处再次等待
5秒后,BOSS线程打印:BOSS:<22:00>可以下班了。再查看是否设置同步条件对象,然后再次设定了同步条件。
5个Work 线程立刻捕捉到设置了同步条件对象,就立刻各自打印 Worker:OhYeah!
因为有join()方法,所以主线程最后打印ending..... 然后立刻退出。
线程:信号量(Semaphare)
信号量是用来控制线程并发数量的,BounderdSemaphare或者Semaphare 管理一个内置的计数器,每当调用acquire()时 -1,
调用release() 时 +1.计数器不能小于0.当计数器为0时,acquire()将阻塞线程至同步锁定状态,直到其他线程release() (类似停车位的概念)
BounderdSemaphare或者Semaphare 的唯一区别在于前者将在调用release()时检查计数器的值是否超过了计数器的初始值,如果超出,会抛出一个异常。
Semaphare 也是锁
锁名=threading.Semaphare(值) #====》创建Semaphare锁,其中值代表的是同时能有几个线程进入锁中,如果不写值,默认值是1
import threading,time
class myThread(threading.Thread):
def run(self):
if semaphore.acquire():#代表了获得semaphore锁
print(self.name)
time.sleep(2)
semaphore.release()#代表释放semaphore锁
if __name__=="__main__":
semaphore=threading.Semaphore(2)#创建semaphore锁,可进入锁的线程数量是2
thrs=[]
for i in range(6):
thrs.append(myThread())##===创建6个子线程
for t in thrs:
t.start()
结果是:先打印2个线程,然后每隔2秒打印2个线程,等总共打印完6个线程立刻结束
Thread-1
Thread-2
Thread-3
Thread-4
Thread-5
Thread-6
*多线程利器:队列(queue)
队列是一种安全的数据结构 ,队列的三种模式:先进先出(FIFO),先进后出(LIFO),优先级队列(PriorityQueue),如果不写,默认是FIFO模式
FIFO是 first in first out 的缩写
LIFO 是last in fist out的缩写
第一种:先进先出:
import queue #====》引入queque
队列名=queue.Queue(X) #===〉创建队列,队列里只能放入 X 个数据。 X代表可放数据数量
队列名.put(s) #===>将数据s放入队列中,数据可以自己定义
#注意:1)队列可放入的数据数量要小于等于X,否则运行会一直卡住,除非get() 掉 数量=多余放入的数据数量 之前已经放入的数据
2)队列名.put(s,block = False):代表当队列满时,会报错
队列名.put(s,block = True):代表党队列满时,会一直在此等待
不写block 默认:block为 True
队列名.get() #====>将队列中的内容取出代码
注意:队列名.get(block = False) 代表:队列里为空会报错,
若不写block = False,默认 block = True :当队列里为空的话,会一直在此等待:
先进先出(FIFO) 例子:
import queue
s = queue.Queue(3) #===========创建队列,设置队列可放3个数据,不设置队列模式,默认是先进先出模式
s.put(12) #====================将数字12加入队列
s.put('hello')#================将字符串hello加入队列
s.put({'name':'yuan'}) #=======将字典{'name':'yuan'}加入队列
s.get(12) #====================取出数字12
s.put(55)#=====================放入数字55
while 1:
data = s.get()
print(data)
print('---->')
结果是:
hello
---->
{'name': 'yuan'}
---->
55
---->
程序不结束,因为当数据都从队列取出,队列为空,线程会一直在此等待数据进入
第二种:先进后出(LifoQueue)
import queue
s = queue.LifoQueue()
s.put(12)
s.put('hello')
s.put({'name':'yuan'})
while 1:
data = s.get()
print(data)
print('---->')
结果是:
{'name': 'yuan'}
---->
hello
---->
12
---->
然后等待数据进入,不结束
第三种:优先级队列(PriorityQueue)
队列名.put( [优先级级别,数据] ) #====》将数据放入优先级队列
优先级级别越低的会先出来
import queue
s = queue.PriorityQueue()
s.put([3,12]) #=====>其中3代表优先级级别
s.put([2,'hello'])
s.put([4,{'name':'yuan'}])
while 1:
data = s.get()
print(data[1])#=====》只取出数据
print('---->')
队列的常用方法
队列名.qsize() 返回队列的大小,即可放入多少个数据。
队列名.empty() 如果队列为空,返回True,反之返回False.
队列名.full() 如果队列满了,返回True,反之False
队列名.get_nowait() 相当于 队列名.get(block = False) 代表队列为空会报错
队列名.put_nowait(数据) 相当于 队列名.put(block = False)代表队列满了会报错
队列名.task_done() 在完成一项工作之后,队列名.task_done() 函数向任务已经完成的队列发送一个信号
队列名.join() 实际上意味着等到队列为空,再执行别的操作
队列名.task_done()与队列名.join() 是一对,队列名.task_done() 发一个信号给队列,队列名.join() 立刻从对列里收信号,然后执行下边代码。如果队列名.task_done() 一直不发信号给队列,队列名.join() 会一直在此阻塞,不往下执行。
如下:
import queue
s = queue.Queue(3)
s.put(12)
s.put('hello')
s.put({'name':'yuan'})
print('队列可放数据数量:',s.qsize()) #======队列可放数据数量: 3
print('队列是否已满:',s.full())#========队列是否已满: True
print('队列是否为空:',s.empty())#=======队列是否为空: False
#s.put_nowait(55)#=====================结果是报错:queue.Full 相当于 s.put(block = False)
while 1:
data = s.get()
print(data)
print('---->')
生产者消费者模型
定义:
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者与消费者的的处理能力。
这就像,在餐厅,厨师做好菜不直接和客户交流,而是交给前台,而客户取饭菜也不需要找厨师,直接去前台领取即可,这也是一个接偶的过程。
import time,random
import queue,threading
q = queue.Queue()
def Producer(name):
count = 0
while count<10:
print('making....')
time.sleep(5)
q.put(count)
print('Producer %s has produced %s baozi..'%(name,count))
count += 1
q.join()#队列会在此一直卡者,一旦Consumer(name)中的q.task_done 执行,队列会变为空,才会往下执行。
print('ok......')
def Consumer(name):
count = 0
while count <10:
time.sleep(random.randrange(4))
data = q.get()
print('eating.....')
time.sleep(5)
print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' %(name, data))
q.task_done()#此处发消息给队列,队列会变为空
count +=1
p1 = threading.Thread(target=Producer, args=('A',))#创建线程p1
c1 = threading.Thread(target=Consumer, args=('B',))#创建线程c1
c2 = threading.Thread(target=Consumer, args=('C',))
c3 = threading.Thread(target=Consumer, args=('D',))
p1.start()
c1.start()
c2.start()
c3.start()