文章目录
一、什么是进程
就是指操作系统能执行多个任务
eg:使用Windows操作系统可以同时听音乐、学习python课程等多个任务。每个任务就是一个进程。
进程四计算机已运行城西的实体
进程和程序不同,程序本身只有指令、数据、及其组织形式的描述,进程才是程序(那些指令和数据)的运行实例。
二、创建进程的常用方法
在python中有多个模块可以创建进程,比较常用的有os.fork()函数
、multiprocessing
模块和Pool
进程池
2.1使用multiprocessing模块创建进程
语法:
Process([group[,target[,name[,args[,kwargs]]]]])
- group:参数未使用,值始终为None
- target:表示当前进程启动时执行的可调用对象
- name:为当前进程实例的别名
- args:表示传递给target函数的参数元组
- kwargs:表示传递给target函数的参数字典
from multiprocessing import Process
# 执行子进程代码
def test(interval):
print('我是子进程')
def main():
print('主进程开始')
p=Process(target=test,args=(1,))
p.start()
print('主进程结束')
if __name__ == '__main__':
main()
>>>
主进程开始
主进程结束
我是子进程
上述代码中p实例除了常用的方法start()外,还有如下方法:
- is_alive(): 判断进程实例是否还在执行
- join([timeout]):是否等待进程实例执行结束,或者等待多少秒
- start():启动进程实例(创建子进程)
- run():如果没有给定target参数,对这个对象调用start()方法时,就将执行对象中的run()方法;
- terminate():不管任务是否完成,立即终止
- name:当前实例别名,默认为Process-N,N为从1开始递增的整数
- pid:当前进程实例的PID值
# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os
#两个子进程将会调用的两个方法
def child_1(interval):
print("子进程(%s)开始执行,父进程为(%s)" % (os.getpid(), os.getppid()))
t_start = time.time() # 计时开始
time.sleep(interval) # 程序将会被挂起interval秒
t_end = time.time() # 计时结束
print("子进程(%s)执行时间为'%0.2f'秒"%(os.getpid(),t_end - t_start))
def child_2(interval):
print("子进程(%s)开始执行,父进程为(%s)" % (os.getpid(), os.getppid()))
t_start = time.time() # 计时开始
time.sleep(interval) # 程序将会被挂起interval秒
t_end = time.time() # 计时结束
print("子进程(%s)执行时间为'%0.2f'秒"%(os.getpid(),t_end - t_start))
if __name__ == '__main__':
print("------父进程开始执行-------")
print("父进程PID:%s" % os.getpid()) # 输出当前程序的ID
p1=Process(target=child_1,args=(1,)) # 实例化进程p1
p2=Process(target=child_2,name="mrsoft",args=(2,)) # 实例化进程p2
p1.start() # 启动进程p1
p2.start() # 启动进程p2
#同时父进程仍然往下执行,如果p2进程还在执行,将会返回True
print("p1.is_alive=%s"%p1.is_alive())
print("p2.is_alive=%s"%p2.is_alive())
#输出p1和p2进程的别名和pid
print("p1.name=%s"%p1.name)
print("p1.pid=%s"%p1.pid)
print("p2.name=%s"%p2.name)
print("p2.pid=%s"%p2.pid)
print("------等待子进程-------")
p1.join() # 等待p1进程结束
p2.join() # 等待p2进程结束
print("------父进程执行结束-------")
>>>第一次执行
------父进程开始执行-------
父进程PID:1980
p1.is_alive=True
p2.is_alive=True
p1.name=Process-1
p1.pid=3304
p2.name=mrsoft
p2.pid=12300
------等待子进程-------
子进程(3304)开始执行,父进程为(1980)
子进程(12300)开始执行,父进程为(1980)
子进程(3304)执行时间为'1.01'秒
子进程(12300)执行时间为'2.01'秒
------父进程执行结束-------
Process finished with exit code 0
>>>第二次执行
------父进程开始执行-------
父进程PID:10104
p1.is_alive=True
p2.is_alive=True
p1.name=Process-1
p1.pid=6004
p2.name=mrsoft
p2.pid=2852
------等待子进程-------
子进程(6004)开始执行,父进程为(10104)
子进程(2852)开始执行,父进程为(10104)
子进程(6004)执行时间为'1.00'秒
子进程(2852)执行时间为'2.00'秒
------父进程执行结束-------
Process finished with exit code 0
2.2 使用Process子类创建进程
对于一些简单的小任务,使用Process(target=test)方式实现多进程。但是使用复杂的进程,通常定义成一个类,使其继承Process类
每次实例化这个类的时候,就等同实例化一个进程对象。
# -*- coding:utf-8 -*-
from multiprocessing import Process
import time
import os
#继承Process类
class SubProcess(Process):
# 由于Process类本身也有__init__初识化方法,这个子类相当于重写了父类的这个方法
def __init__(self,interval,name=''):
Process.__init__(self) # 调用Process父类的初始化方法
self.interval = interval # 接收参数interval
if name: # 判断传递的参数name是否存在
self.name = name # 如果传递参数name,则为子进程创建name属性,否则使用默认属性
#重写了Process类的run()方法
def run(self):
print("子进程(%s) 开始执行,父进程为(%s)"%(os.getpid(),os.getppid()))
t_start = time.time()
time.sleep(self.interval)
t_stop = time.time()
print("子进程(%s)执行结束,耗时%0.2f秒"%(os.getpid(),t_stop-t_start))
if __name__=="__main__":
print("------父进程开始执行-------")
print("父进程PID:%s" % os.getpid()) # 输出当前程序的ID
p1 = SubProcess(interval=1,name='mrsoft')
p2 = SubProcess(interval=2)
#对一个不包含target属性的Process类执行start()方法,就会运行这个类中的run()方法,所以这里会执行p1.run()
p1.start() # 启动进程p1
p2.start() # 启动进程p2
# 输出p1和p2进程的执行状态,如果真正进行,返回True,否则返回False
print("p1.is_alive=%s"%p1.is_alive())
print("p2.is_alive=%s"%p2.is_alive())
#输出p1和p2进程的别名和pid
print("p1.name=%s"%p1.name)
print("p1.pid=%s"%p1.pid)
print("p2.name=%s"%p2.name)
print("p2.pid=%s"%p2.pid)
print("------等待子进程-------")
p1.join() # 等待p1进程结束
p2.join() # 等待p2进程结束
print("------父进程执行结束-------")
>>>
------父进程开始执行-------
父进程PID:19576
p1.is_alive=True
p2.is_alive=True
p1.name=mrsoft
p1.pid=20424
p2.name=SubProcess-2
p2.pid=19304
------等待子进程-------
子进程(20424) 开始执行,父进程为(19576)
子进程(19304) 开始执行,父进程为(19576)
子进程(20424)执行结束,耗时1.01秒
子进程(19304)执行结束,耗时2.00秒
------父进程执行结束-------
2.3 使用进程池Pool创建进程
当需要创建的⼦进程数量不多时, 可以直接利⽤multiprocessing.Process动态生成多个进程, 但如果要创建很多进程时,⼿动创建的话⼯作量会非常大,此时就可以⽤到multiprocessing
模块提供的Pool去创建一个进程池。
multiprocessing.Pool常⽤函数:
- apply_async(func, args, kwds):使⽤⾮阻塞⽅式调⽤func(任务并⾏执⾏),args为传递给func的参数列表,kwds为传递给func的关键字参数列表
- apply(func, args, kwds):使⽤阻塞⽅式调⽤func,必须等待上⼀个进程执行完任务后才能执⾏下⼀个进程,了解即可,几乎不用
- close():关闭Pool,使其不再接受新的任务
- terminate():不管任务是否完成,⽴即终⽌
- join():主进程阻塞,等待⼦进程的退出,必须在close或terminate之后使⽤
初始化Pool时,可以指定⼀个最⼤进程数,当有新的任务提交到Pool中时,如果进程池还没有满,那么就会创建⼀个新的进程⽤来执⾏该任务,但如果进程池已满(池中的进程数已经达到指定的最⼤值),那么该任务就会等待,直到池中有进程结束才会创建新的进程来执⾏。
# -*- coding=utf-8 -*-
from multiprocessing import Pool
import os, time
def task(name):
print('子进程(%s)执行task %s ...' % ( os.getpid() ,name))
time.sleep(1) # 休眠1秒
if __name__=='__main__':
print('父进程(%s).' % os.getpid())
p = Pool(3)
for i in range(10): # 从0开始循环10次
p.apply_async(task, args=(i,)) # 使用非阻塞方式调用task()函数
print('等待所有子进程结束...')
p.close() # 关闭进程池,关闭后p不再接收新的请求
p.join() # 等待子进程结束
print('所有子进程结束.')
>>>
父进程(22220).
等待所有子进程结束...
子进程(24124)执行task 0 ...
子进程(20972)执行task 1 ...
子进程(17992)执行task 2 ...
子进程(24124)执行task 3 ...
子进程(17992)执行task 4 ...
子进程(20972)执行task 5 ...
子进程(17992)执行task 6 ...
子进程(24124)执行task 8 ...
子进程(20972)执行task 7 ...
子进程(20972)执行task 9 ...
所有子进程结束.
这里看到的是不一定是顺序的
三、进程间通讯
3.1 队列简介
队列就是模仿现实中的排队
3.2 多进程队列的使用
可以使用 multiprocessing
模块的Queue实现多进程之间的数据传递。
初始化Queue()对象(例如:q=Quere(num)),若括号中没有指定最大可接受的信息数量,或数量为负值,那么久代表可接受的消息数量没有上线(直到内存的尽头)
- Queue. qsize():返回当前队列包含的消息数量
- Queue. empty():如果队列为空,返回Ture,反之返回False
- Queue. full():如果队列满了,返回Ture,反之返回False
- Queue. get([block[,timeout]]):获取队列中的一条消息,然后将其从队列中移除,block默认值为Ture
1.如果block使用默认值,且没有设置timeout(单位,秒),消息队列为空,此时程序将被阻塞(停在读取状态),直到从消息队列读到信息为止,如果设置了timeout,则会等待timeout秒,若还没有读到任何信息,则抛出Queue.Empty异常 2.如果block值为False,消息队列为空,则会立即抛出Queue.Empty异常
- Queue. get_nowait():相当于Queue.get(False)
- Queue.put(item,[block[,timeout]]):将item消息写入队列,block默认值为Ture
1.如果block使用默认值,且没有设置timeout(单位,秒),消息队列如果已经没有空间写入,此时程序将被阻塞(停在写入状态),直到从消息队列腾出空间为止,如果设置了timeout,则会等待timeout秒,若还没有空间,则抛出Queue.Full异常 2.如果block值为False,消息队列没有空间写入,则会立即抛出Queue.Full异常
- Queue.put_nowait(item):相当于Queue.put(item,False)
# -*- coding: utf-8 -*-
from multiprocessing import Process, Queue
# 向队列中写入数据
def write_task(q):
if not q.full():
for i in range(5):
message = "消息" + str(i)
q.put(message)
print("写入:%s"%message)
# 从队列读取数据
def read_task(q):
while not q.empty():
print("读取:%s" % q.get(True,2)) # 等待2秒,如果还没读取到任何消息,则抛出"Queue.Empty"异常
if __name__ == "__main__":
print("-----父进程开始-----")
q = Queue() # 父进程创建Queue,并传给各个子进程
pw = Process(target=write_task, args=(q,)) # 实例化写入队列的子进程,并且传递队列
pr = Process(target=read_task, args=(q,)) # 实例化读取队列的子进程,并且传递队列
pw.start() # 启动子进程 pw,写入
pr.start() # 启动子进程 pr,读取
pw.join() # 等待 pw 结束
pr.join() # 等待 pr 结束
print("-----父进程结束-----")
>>>
-----父进程开始-----
写入:消息0
写入:消息1
写入:消息2
写入:消息3
写入:消息4
-----父进程结束-----
四、什么是线程
一个任务比作一个进程,一个进程有多个线程组成
线程是操作系统能够进行运算调度的最小单位
五、创建线程
由于线程是操作系统直接支持的执行单位,因此高级语言(python、java)通常内置了对线程的支持。python的标准库提供了两个模块:_thread和threading,_thread是低级模块,threading是高级模块,对_thread进行了封装。绝大多数我们只要用threading这个高级模块
5.1 使用threading模块创建线程
threading模块提供了一个Thread类来代表一个线程对象,语法如下
Thread([group[,target[,name[,args[,kwargs]]]]])
- group:值为none,为以后版本而保留
- target:表示一个可调用对象,线程启动的时候,run()方法将调用此对象,默认值为None,表示不调用任何内容
- name:表示当前线程的名称,默认创建一个Thread-N格式的唯一名称
- args:表示传递给target函数的参数元组
- kwargs:表示传递给target函数的参数字典
# -*- coding: utf-8 -*-
import threading,time
def process():
for i in range(3):
time.sleep(1)
msg = "子线程"+threading.current_thread().name+'执行,i='+str(i) #name属性中保存的是当前线程的名字
print(msg)
if __name__ == '__main__':
print('-----主线程开始-----')
threads=[threading.Thread(target=process) for i in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print('-----主线程结束-----')
》》》
-----主线程开始-----
子线程Thread-3执行,i=0
子线程Thread-4执行,i=0
子线程Thread-2执行,i=0
子线程Thread-1执行,i=0
子线程Thread-3执行,i=1
子线程Thread-4执行,i=1
子线程Thread-1执行,i=1
子线程Thread-2执行,i=1
子线程Thread-4执行,i=2
子线程Thread-3执行,i=2
子线程Thread-1执行,i=2
子线程Thread-2执行,i=2
-----主线程结束-----
5.2 使用Thread子类创建线程
Thread 线程类和Process进程类的使用方式费城相似,也可以定义一个子类,使其继承Thread线程类来创建线程。
# -*- coding: utf-8 -*-
import threading
import time
class SubThread(threading.Thread):
def run(self):
for i in range(3):
time.sleep(1)
msg = "子线程"+self.name+'执行,i='+str(i) #name属性中保存的是当前线程的名字
print(msg)
if __name__ == '__main__':
print('-----主线程开始-----')
t1 = SubThread() # 创建子线程t1
t2 = SubThread() # 创建子线程t2
t1.start() # 启动子线程t1
t2.start() # 启动子线程t2
t1.join() # 等待子线程t1
t2.join() # 等待子线程t2
print('-----主线程结束-----')
>>>
-----主线程开始-----
子线程Thread-2执行,i=0
子线程Thread-1执行,i=0
子线程Thread-2执行,i=1
子线程Thread-1执行,i=1
子线程Thread-2执行,i=2
子线程Thread-1执行,i=2
-----主线程结束-----
六、线程间通信
线程之间可以通行,可以定义一个全局变量,让后不同线程调用这变量,可以共享数据
6.1 什么是互斥锁
就是防止多个线程同时读写某一块内存区域。互斥锁为资源引入一个状态:锁定和非锁定
6.2 使用互斥锁
mutex=threading.Lock() # 创建锁
mutex.acquire([blocking]) # 锁定
mutex.release() # 释放锁
acquire([blocking]) : 获取锁,如果有必要,需要阻塞到锁释放为止。如果提供blocking参数,并将它设置为False,当前无法获取锁定时候立即返回False,如果成功获取锁定则返回True
from threading import Thread,Lock
import time
n=100 # 共100张票
def task():
global n
mutex.acquire() # 上锁
temp=n # 赋值给临时变量
time.sleep(0.1) # 休眠0.1秒
n=temp-1 # 数量减1
print('购买成功,剩余%d张电影票'%n)
mutex.release() # 释放锁
if __name__ == '__main__':
mutex=Lock() # 实例化Lock类
t_l=[] # 初始化一个列表
for i in range(10):
t=Thread(target=task) # 实例化线程类
t_l.append(t) # 将线程实例存入列表中
t.start() # 创建线程
for t in t_l:
t.join() # 等待子线程结束
>>>
购买成功,剩余99张电影票
购买成功,剩余98张电影票
购买成功,剩余97张电影票
购买成功,剩余96张电影票
购买成功,剩余95张电影票
购买成功,剩余94张电影票
购买成功,剩余93张电影票
购买成功,剩余92张电影票
购买成功,剩余91张电影票
购买成功,剩余90张电影票
6.3 使用队列在线程间通信
一种生产者和消费者模式
# -*- coding: utf-8 -*-
from queue import Queue
import random,threading,time
#生产者类
class Producer(threading.Thread):
def __init__(self, name,queue):
threading.Thread.__init__(self, name=name)
self.data=queue
def run(self):
for i in range(5):
print("生产者%s将产品%d加入队列!" % (self.getName(), i))
self.data.put(i)
time.sleep(random.random())
print("生产者%s完成!" % self.getName())
#消费者类
class Consumer(threading.Thread):
def __init__(self,name,queue):
threading.Thread.__init__(self,name=name)
self.data=queue
def run(self):
for i in range(5):
val = self.data.get()
print("消费者%s将产品%d从队列中取出!" % (self.getName(),val))
time.sleep(random.random())
print("消费者%s完成!" % self.getName())
if __name__ == '__main__':
print('-----主线程开始-----')
queue = Queue() # 实例化队列
producer = Producer('Producer',queue) # 实例化线程Producer,并传入队列作为参数
consumer = Consumer('Consumer',queue) # 实例化线程Consumer,并传入队列作为参数
producer.start() # 启动线程Producer
consumer.start() # 启动线程Consumer
producer.join() # 等待线程Producer结束
consumer.join() # 等待线程Consumer结束
print('-----主线程结束-----')
>>>
-----主线程开始-----
生产者Producer将产品0加入队列!
消费者Consumer将产品0从队列中取出!
生产者Producer将产品1加入队列!
消费者Consumer将产品1从队列中取出!
生产者Producer将产品2加入队列!
消费者Consumer将产品2从队列中取出!
生产者Producer将产品3加入队列!
生产者Producer将产品4加入队列!
生产者Producer完成!
消费者Consumer将产品3从队列中取出!
消费者Consumer将产品4从队列中取出!
消费者Consumer完成!
-----主线程结束-----
Process finished with exit code 0