了解什么是程序与进程
掌握创建进程的方式
掌握进程之间的通信
了解进程与线程的概述
掌握创建线程的方式
掌握线程之间的通信方式
掌握生产者与消费者模式
创建进程的方式
方法 / 属性名称 | 功能描述 |
name | 当前进程实力别名,默认为Process-N |
pid | 当前进程对象的PID值 |
is_alive() | 进程是否执行完,没执行完结果为True,否则为False |
join(timeout) | 等待结束或等待timeout秒 |
start() | 启动进程 |
run() | 如果没有指定target参数,则启动进程后,会调用父类中的run方法 |
terminate() | 强制终止进程 |
第一种创建进程
语法结构:
Process(group=Npne,target,name,args,kwargs)
参数说明:
(1)group:表示分组,实际上不使用,默认值为None即可
(2)target:表示子进程要执行的任务,支持函数名
(3)name:表示子进程的名称
(4)args:表示调用函数的参数位置,以元组的形式进行传递
(5)kwargs:表示调用函数的关键字参数,以字典的形式进行
from multiprocessing import Process
import os,time
#函数中的代码是子进程要执行的代码
def test():
print(f'我是子进程,我的PIP是:{os.getpid()},我的父进程是:{os.getppid()}')
time.sleep(1)
if __name__ == '__main__':
print('主进程开始执行')
lst=[]
# 创建5个进程
for i in range(5):
# 创建子进程
p = Process(target=test)
# 将启动的进程添加到列表中
p.start()
#启动中的进程添加到列表中
lst.append(p)
# 遍历列表中的子进程
for item in lst:
item.join()#阻塞主进程
print('主进程结束')
第二种创建进程
语法结构:
class 子进程(Process):
pass
from multiprocessing import Process
import os,time
#自定义一个类
class SubProcess(Process):
def __init__(self,name):
super().__init__()
self.name=name
def run(self):
print(f'子进程的名称{self.name},PID:{os.getpid()},父进程的PID:{os.getppid()}')
if __name__ == '__main__':
print('父进程开始执行')
lst=[]
for i in range(1,6):
p1=SubProcess(f'进程:{i}')
#启动进程
p1.start()
lst.append(p1)
for item in lst:
item.join()
print('父进程执行结束')
Pool 进程池
进程池的原理是创建一个进程池,并设置进程池中最大的进程数量。假设进程池中最大的进程数为3,现在有10个任务需要执行,那么进程池一次可以执行3个任务,4次即可完成全部任务的执行。
创建进程池的语法结构:
进程池对象=Pool(N)
方法名 | 功能描述 |
apply_async(func,args,kwargs) | 使用非阻塞方式调用函数func |
apply(func,args,kwargs) | 使用阻塞方式调用函数func |
close() | 关闭进程池,不再接收新任务 |
terminate() | 不管任务是否完成,立即终止 |
join() | 阻塞主进程,必须在terminate()或close()之后使用 |
进程之间的通信
进程之间可以通过队列(Queue)进行通信,队列是一种先进先出(First In First Out)的数据结构。
方法名称 | 功能描述 |
qsize() | 获取当前队列包含的信息数量 |
empty() | 判断队列是否为空,为空结果为True,否则为False |
full() | 判断队列是否满了,满结果为True,否则为False |
get(block_True) | 获取队列中的一条消息,然后从队列中移除,block默认值为True |
get_nowait() | 相当于get(block_False),消息队列为空时抛出异常 |
put(item,block-True) | 将item消息放入队列,block默认为True 消息队列满时会排队等待直到有空位时加入 |
put_nowait(item) | 相当于put(item,block=False) 消息队列满时报错 |
队列示例:
from multiprocessing import Queue,Process
import time
a=100
def write_msg(q):# q是队列
global a
if not q.full():
for i in range(6):
a-=10
q.put(a)# 入队
print('a入队时的值:',a)
# 出队
def read_msg(q):
time.sleep(1)
if not q.empty():
print('出队时a的值:',q.get())
if __name__ == '__main__':
print('父进程开始执行')
q=Queue()# 由父进程创建队列,没有指定参数,说明可接收消息的个数没有上限
# 创建两个子进程
p1=Process(target=write_msg,args=(q,))
p2=Process(target=read_msg,args=(q,))
# 启动两个子进程
p1.start()
p2.start()
# 等待进程执行结束
p1.join()
p2.join()
print('父进程执行完毕')
创建线程的方式
函数式创建线程
语法结构:
t=Thread(group,target,name,args,kwargs)
参数说明:
(1)group:创建线程对象的进程组
(2)target:创建的线程对象所要执行的目的函数
(3)name:创建线程对象的名称,默认为“Thread-n”
(4)args:用元组以位置参数的形式传入target对应函数的参数
(5)kwargs:用字典以关键字参数的形式传入target对应函数的参数
from threading import Thread
import threading
import time
def text():
for i in range(3):
time.sleep(3)
print(f'线程:{threading.current_thread().name}正在执行')
if __name__ == '__main__':
start = time.time()
print('主线程开始执行')
# 创建线程
lst=[Thread(target=text) for i in range(2)]
for item in lst:# item的数据类型是Thread类型
item.start()
for item in lst:
item.join()
print(f'一共耗时:{time.time() - start}')
使用Thread子类创建线程
操作步骤:
(1)自定义类继承 threading 模块下的 Thread 类
(2)实现 run 方法
import threading
from threading import Thread
import time
class SubThread(Thread):
def run(self):
for i in range(3):
time.sleep(1)
print(f'线程:{threading.current_thread().name}正在执行{i}')
if __name__ == '__main__':
print('主线程开始执行')
lst = [SubThread() for i in range(2)]
for i in lst:
i.start()
for i in lst:
i.join()
print('主线程运行完毕')
线程操作共享数据的安全性问题
举例:
import threading
from threading import Thread
import time
ticket=50# 代表有40张票
def sale_ticket():
global ticket
# 每个排队窗口有100人
for i in range(100):# 每个线程要执行100次循环
if ticket>0:
print(f'{threading.current_thread().name}正在出售第{ticket}张票')
ticket-=1
time.sleep(1)
if __name__ == '__main__':
for i in range(3):# 创建三个线程,代表3个窗口
t=Thread(target=sale_ticket)
t.start()
以上代码运行时会出先多个进程抢一个目标的情况(不安全)
Lock锁
当一个线程操作一个共享数据时,会给共享数据上锁,其他线程到达时只能等待。之后共享资源的锁被打开时才能被访问
导入 Lock ,创建锁对象(对象名=Lock()),在合适的位置上锁(对象名.acquire()),在合适的位置释放锁(对象名.release())
import threading
from threading import Thread, Lock
import time
ticket = 50 # 代表有40张票
lock_obj = Lock() # 创建锁对象
def sale_ticket():
global ticket
# 每个排队窗口有100人
for i in range(100): # 每个线程要执行100次循环
lock_obj.acquire() # 上锁
if ticket > 0:
print(f'{threading.current_thread().name}正在出售第{ticket}张票')
ticket -= 1
time.sleep(0.1)
lock_obj.release() # 释放锁
if __name__ == '__main__':
for i in range(3): # 创建三个线程,代表3个窗口
t = Thread(target=sale_ticket)
t.start()
生产者与消费者模式
生产者与消费者模式是线程模型中的经典问题,与编程语言无关。当程序中出现了明确的两类任务,一个任务负责生产数据,一个任务负责处理生产的数据时就可以使用该模式。
Python中的内置模块queue中的Queue类
方法名称 | 功能描述 |
put(item) | 向队列中放置数据,如果队列为满,则阻塞 |
get() | 从队列中取走数据,如果队列为空,则阻塞 |
join() | 如果队列不为空,则等待队列变为空 |
task_done() | 消费者熊队列中取走一项数据,当队列变为空时,唤醒调用join()的线程 |
from queue import Queue
from threading import Thread
import time
class Producer(Thread):
def __init__(self,name,queue):
Thread.__init__(self,name=name)
self.queue = queue
def run(self):
for i in range(1,6):
print(f'{self.name} 将产品{i}放入队列')
self.queue.put(i)
time.sleep(1)
print('生产者完成了所有数据的存放')
class Consumer(Thread):
def __init__(self,name,queue):
Thread.__init__(self,name=name)
self.queue = queue
def run(self):
for i in range(5):
value = self.queue.get()
print(f'消费者线程{self.name}取出了{value}')
time.sleep(1)
print('------消费者线程完成了所有数据的取出------')
if __name__ == '__main__':
queue=Queue()
p=Producer('Producer',queue)
c=Consumer('Consumer',queue)
p.start()
c.start()
p.join()
c.join()
print('主线程执行结束')