程序和进程
使用multiprocessing创建线程
'''
程序:英文单词为Program,是指一系列有序指令的集合,使用编程语言所编写,用于实现一定的功能
进程:是指启动后的程序,系统会为进程分配内存空间
创建进程的方式
第一种创建进程的语法结构:Process(group=None,target,name,args,kwargs)
参数说明:
1.group:表示分组,实际上不使用,值默认为None即可
2.target:表示子进程要执行的任务,支持函数名
3.name:表示子进程的名称
4.args:表示调用函数的位置参数,以元组的形式进行传递
5.kwargs:表示调用函数的关键字参数,以字典的形式进行传递
第二种创建进程的语法结构:
class 子进程(Process):
pass
'''
import os
import time
from multiprocessing import Process
def test():
print(f'我是子进程,我的PIP是:{os.getpid()},我的父进程是:{os.getppid()}')
time.sleep(1)
if __name__ == '__main__':
print('主进程开始执行')
lst=[]
#创建子进程
for i in range(5):
p= Process(target=test)
#启动子进程
p.start()
#启动中的进程添加到列表中
lst.append(p)
#遍历列表,列表中五个子进程
for item in lst:#item的数据类型是Process
item.join()#阻塞主进程
print('主进程执行结束')
#主进程要等到所有的子进程执行完毕之后,主进程才会执行结束
Process类的属性和方法
import os
import time
from multiprocessing import Process
'''
Process常用方法/属性
方法/属性名称: 功能描述
name: 当前进程实例别名,默认为Process-N
pid: 当前进程对象的PID值
is_alive(): 进程是否执行完,没执行完结果为True,否则为False
join(timeout): 等待结束或等待timeout秒
start(): 启动进程
run(): 如果没有指定target参数,则启动进程后,会调用父类中的run方法
terminate(): 强制终止进程
'''
# 函数式方式创建子进程
def sub_process(name):
print(f'我是子进程,我的PIP是:{os.getpid()},我的父进程是:{os.getppid()}----------------------------{name}')
def sub_process2(name):
print(f'我是子进程,我的PIP是:{os.getpid()},我的父进程是:{os.getppid()}----------------------------{name}')
if __name__ == '__main__':
# 主进程
for i in range(5):
# 创建第一个子进程
p1 = Process(target=sub_process, args=('ll',))
p2 = Process(target=sub_process2, args=(18,))
p1.start()
p2.start()
print(p1.name, 'pid是:', p1.pid)
print(p2.name, 'pid是:', p2.pid)
p1.join() # 主进程要等p1执行结束,阻塞主进程
p2.join() # 主进程要等p2执行结束,阻塞主进程
print(p1.name, '是否执行完毕', p1.is_alive())
print(p2.name, '是否执行完毕', p2.is_alive())
print('父进程执行完毕')
import os
import time
from multiprocessing import Process
'''
Process常用方法/属性
方法/属性名称: 功能描述
name: 当前进程实例别名,默认为Process-N
pid: 当前进程对象的PID值
is_alive(): 进程是否执行完,没执行完结果为True,否则为False
join(timeout): 等待结束或等待timeout秒
start(): 启动进程
run(): 如果没有指定target参数,则启动进程后,会调用父类中的run方法
terminate(): 强制终止进程
'''
# 函数式方式创建子进程
def sub_process(name):
print(f'我是子进程,我的PIP是:{os.getpid()},我的父进程是:{os.getppid()}----------------------------{name}')
def sub_process2(name):
print(f'我是子进程,我的PIP是:{os.getpid()},我的父进程是:{os.getppid()}----------------------------{name}')
if __name__ == '__main__':
# 主进程
print('父进程执行开始')
for i in range(5):
# 创建第一个子进程
p1 = Process(target=sub_process,args=('LL',))#没有指定target参数,不会执行自己编写的函数中的代码,会调用Precess类中的run方法
p2 = Process(target=sub_process2,args=(18,))
p1.start()#如果Process类创建的时候没有指定target参数调用Precess类中的run方法
p2.start()#如果Process类创建的时候指定了target参数 start()调用target指定的函数去执行
# print(p1.name, 'pid是:', p1.pid)
# print(p2.name, 'pid是:', p2.pid)
#
# p1.join() # 主进程要等p1执行结束,阻塞主进程
# p2.join() # 主进程要等p2执行结束,阻塞主进程
#
# print(p1.name, '是否执行完毕', p1.is_alive())
# print(p2.name, '是否执行完毕', p2.is_alive())
#终止进程
p1.terminate()
p2.terminate()
print('父进程执行完毕')
'''
Process常用方法/属性
方法/属性名称: 功能描述
name: 当前进程实例别名,默认为Process-N
pid: 当前进程对象的PID值
is_alive(): 进程是否执行完,没执行完结果为True,否则为False
join(timeout): 等待结束或等待timeout秒
start(): 启动进程
run(): 如果没有指定target参数,则启动进程后,会调用父类中的run方法
terminate(): 强制终止进程
'''
import os
import time
from multiprocessing import Process
#自定义一个类
class SubProcess(Process):
#编写一个初始化方法
def __init__(self,name):
super().__init__()
self.name = name
#重写父类中的run方法
def run(self):
print(f'我是子进程,我的PIP是:{os.getpid()},我的父进程是:{os.getppid()}----------------------------{self.name}')
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()之后使用
'''
from multiprocessing import Pool
import time, os
# 编写任务
def task(name):
print(f'子进程的PID: {os.getpid()},执行的任务: {name}')
time.sleep(1)
if __name__ == '__main__':
# 主进程
start = time.time()
print('主进程开始执行')
#创建进程池
p=Pool(3)
#创建任务
for i in range(10):
#以非阻塞的方式
p.apply_async(func=task,args=(i,))
p.close()#关闭进程池不再接受新任务
p.join()#阻塞父进程,等待所有的子进程执行完毕后,才会执行父进程中的代码
print('所有的子进程执行完毕,父进程执行结束')
print(time.time()-start)
阻塞
'''
Pool进程池
进程池原理:创建一个进程池,并设置进程池最大的进程数量,假设进程池中最大的进程数为3,现在有10个任务需要执行,那么进程池一次可以执行3个任务,4次即可完成全部人物的执行
语法结构: 进程池对象 = Pool(N)
方法名: 功能描述
apply_async(func,args,kwargs): 使用非阻塞方式调用函数func
apply(func,args,kwargs): 使用阻塞方式调用函数func
close(): 关闭进程池,不再接受新的任务
terminate(): 不管任务是否完成,立即终止
join(): 阻塞主进程,必须在terminate()或close()之后使用
'''
from multiprocessing import Pool
import time, os
# 编写任务
def task(name):
print(f'子进程的PID: {os.getpid()},执行的任务: {name}')
time.sleep(1)
if __name__ == '__main__':
# 主进程
start = time.time()
print('主进程开始执行')
#创建进程池
p=Pool(3)
#创建任务
for i in range(10):
#以阻塞的方式
p.apply(func=task,args=(i,))
p.close()#关闭进程池不再接受新任务
p.join()#阻塞父进程,等待所有的子进程执行完毕后,才会执行父进程中的代码
print('所有的子进程执行完毕,父进程执行结束')
print(time.time()-start)
进程间数据共享
- 进程间数据共享
'''
并发和并行
并发:是指两个或多个事件同一时间间隔发生,多个任务被交替乱换着执行,比如A事件是吃苹果,在吃苹果的过程中有快递员敲门让你手下快递,收快递是B事件,那么收完快递继续吃没吃完的苹果就是并发
并行:指两个或多个事件在同一时刻发生,多个任务在同一时刻在多个处理器上同时执行,比如A事件是泡脚,B事件是打电话,C事件是记录电话内容,这三件事则可以在同一时刻发生,这就是并行
进程之间的通信
进程之间可以通过队列(Queue)进行通信,队列是一种先进先出(First In First Out)的数据结构
创建队列的语法结构:队列对象=Queue(N)
队列常用的方法:
方法名称: 功能描述
qsize(): 获取当前队列包含的消息数量
empty(): 判断队列是否为空,为空结果返回Ture,狗则为False
full(): 判断队列是否慢了,满结果为Ture,狗则为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 Pool, Process
import time, os
a = 100
def add():
print('子进程1开始执行')
global a
a += 30
print(f'子进程1 a=: {a}')
print('进程1执行完毕')
def sub():
print('子进程2开始执行')
global a
a -= 50
print(f'子进程2 a=: {a}')
print('进程2执行完毕')
if __name__ == '__main__':
# 父进程
print(f'父进程开始执行a=: {a}')
# 创建加法的子进程
p1 = Process(target=add)
# 创建剑法的子进程
p2 = Process(target=sub)
# 启动子进程
p1.start()
p2.start()
# 阻塞主进程
p1.join()
p2.join()
print(f'父进程执行结束a=: {a}')
- 队列的基本使用
'''
并发和并行
并发:是指两个或多个事件同一时间间隔发生,多个任务被交替乱换着执行,比如A事件是吃苹果,在吃苹果的过程中有快递员敲门让你手下快递,收快递是B事件,那么收完快递继续吃没吃完的苹果就是并发
并行:指两个或多个事件在同一时刻发生,多个任务在同一时刻在多个处理器上同时执行,比如A事件是泡脚,B事件是打电话,C事件是记录电话内容,这三件事则可以在同一时刻发生,这就是并行
进程之间的通信
进程之间可以通过队列(Queue)进行通信,队列是一种先进先出(First In First Out)的数据结构
创建队列的语法结构:队列对象=Queue(N)
队列常用的方法:
方法名称: 功能描述
qsize(): 获取当前队列包含的消息数量
empty(): 判断队列是否为空,为空结果返回Ture,狗则为False
full(): 判断队列是否慢了,满结果为Ture,狗则为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
if __name__ == '__main__':
# 创建队列
q = Queue(3) # 最多可以接收三条信息
print('队列是否为空:', q.empty()) # True
print('队列是否已满:', q.full()) # False
# 向队列中添加信息
q.put('hello')
q.put('world')
print('队列是否为空:', q.empty()) # False
print('队列是否已满:', q.full()) # False
q.put('Python')
print('队列是否为空:', q.empty()) # False
print('队列是否已满:', q.full()) # True
print('队列当中的信息的个数:', q.qsize()) # 3
#出队
print('出队:',q.get())
print('队列当中的信息的个数:', q.qsize()) # 2
#入队
q.put_nowait('html')
# q.put_nowait('sql')#抛出异常 queue.Full
# q.put('sql') #不报错,但是会一直等待,等到队列中有空位置为止
#遍历
if not q.empty():
for i in range(q.qsize()):
print(q.get_nowait())
print('队列是否为空:', q.empty()) # True
print('队列是否已满:', q.full()) # False
print('队列当中的信息的个数:', q.qsize()) # 0
- 队列中添加数据
'''
队列常用的方法:
方法名称: 功能描述
qsize(): 获取当前队列包含的消息数量
empty(): 判断队列是否为空,为空结果返回Ture,狗则为False
full(): 判断队列是否慢了,满结果为Ture,狗则为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
if __name__ == '__main__':
# 创建队列
q = Queue(3) # 最多可以接收三条信息
# 向队列中添加信息 入队
q.put('hello')
q.put('world')
q.put('Python')
# 队列已满,此时再次put数据,会一直等待
# q.put('html')
# 队列已满,此时再次put数据,会一直等待===>修改put的timeout参数
q.put('html',block=True,timeout=2)#等待2秒后超时,抛出异常
- 使用队列实现进程之间的通信
'''
并发和并行
并发:是指两个或多个事件同一时间间隔发生,多个任务被交替乱换着执行,比如A事件是吃苹果,在吃苹果的过程中有快递员敲门让你手下快递,收快递是B事件,那么收完快递继续吃没吃完的苹果就是并发
并行:指两个或多个事件在同一时刻发生,多个任务在同一时刻在多个处理器上同时执行,比如A事件是泡脚,B事件是打电话,C事件是记录电话内容,这三件事则可以在同一时刻发生,这就是并行
进程之间的通信
进程之间可以通过队列(Queue)进行通信,队列是一种先进先出(First In First Out)的数据结构
创建队列的语法结构:队列对象=Queue(N)
队列常用的方法:
方法名称: 功能描述
qsize(): 获取当前队列包含的消息数量
empty(): 判断队列是否为空,为空结果返回Ture,狗则为False
full(): 判断队列是否慢了,满结果为Ture,狗则为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)
while 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('父进程执行结束')
线程
函数方式创建线程
'''
线程:
线程时CPU可调度的最小单位,被包含在进程中,是进程中时机的运作单位,一个进程中可以拥有N多个线程并发执行,而每个线程并行执行不同的任务
创建线程的方式
函数式创建线程的语法结构: t=Thread(group,target,name,args,kwargs)
参数说明:
group:创建线程对象的进程组
target:创建线程对象所要执行的目标函数
name:创建线程对象的名称,默认为Tread-N
args:用元组以位置参数的形式传入target对应函数的参数
kwargs::用字典以关键字参数的形式传入target对应函数的参数
使用Thread子类创建线程的操作步骤
1.自定义类集成threding模块下的Thread类
2.实现run方法
'''
import threading
from threading import Thread
import time
# 编写函数
def test():
for i in range(3):
time.sleep(1)
print(f'线程:{threading.current_thread().name}正在执行{i}')
if __name__ == '__main__':
start = time.time()
print('主线程开始执行')
# 线程
lst = [Thread(target=test) for i in range(2)]
for item in lst:
# 启动线程
item.start()
for item in lst:
item.join()
print(f'一共耗时:{time.time() - start}')
#三个线程并行执行的任务是什么? 主线程负责执行 main中的代码,Thread-1线程 执行三次循环,Thread-2线程 执行三次循环
#三个线程又是并发执行,谁先执行不固定
使用Thread子类创建线程
'''
线程:
线程时CPU可调度的最小单位,被包含在进程中,是进程中时机的运作单位,一个进程中可以拥有N多个线程并发执行,而每个线程并行执行不同的任务
创建线程的方式
函数式创建线程的语法结构: t=Thread(group,target,name,args,kwargs)
参数说明:
group:创建线程对象的进程组
target:创建线程对象所要执行的目标函数
name:创建线程对象的名称,默认为Tread-N
args:用元组以位置参数的形式传入target对应函数的参数
kwargs::用字典以关键字参数的形式传入target对应函数的参数
使用Thread子类创建线程的操作步骤
1.自定义类集成threding模块下的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__':
start = time.time()
print('主线程开始执行')
# 线程
lst = [SubThread() for i in range(2)]
for item in lst:
# 启动线程
item.start()
for item in lst:
item.join()#阻塞主线程
print(f'一共耗时:{time.time() - start}')
'''
线程之间的通信
'''
线程间数据共享
- 线程间是否支持共享数据
from threading import Thread
a=100#定义一个全局变量
def add():
print('加线程开始执行')
global a
a+=30
print(f'加线程a的值为:{a}')
print('加线程执行结束')
def sub():
print('减线程开始执行')
global a
a -= 50
print(f'减线程a的值为:{a}')
print('减线程执行结束')
if __name__ == '__main__':
print('主线程开始执行')
print(f'--------主线程开始执行全局变量a的值:{a}')
#线程
add_thread = Thread(target=add)
sub_thread = Thread(target=sub)
#执行线程
add_thread.start()
sub_thread.start()
#阻塞主线程
add_thread.join()
sub_thread.join()
print('主线程执行结束')
print(f'--------主线程执行结束全局变量a的值:{a}')
'''
线程之间是可以进行数据共享的,需要注意进程之间不会进行共享
'''
- 线程间共享数据的安全问题
import threading
from threading import Thread
import time
ticket = 50 # 定义一个变量,表示当前存在50张票
def sale_ticket():
global ticket
# 每个排队窗口假设存在100个人
for i in range(100):
if ticket > 0:
print(f'{threading.current_thread().name}正在出售第{ticket}张票')
ticket -= 1
time.sleep(1)
if __name__ == '__main__':
for i in range(3): # 创建三个线程,代码三个窗口
t = Thread(target=sale_ticket)
t.start()
- 使用Lock锁解决线程共享数据的安全问题
import threading
from threading import Thread,Lock
import time
ticket = 50 # 定义一个变量,表示当前存在50张票
lock_obj=Lock()#创建锁对象
def sale_ticket():
global ticket
# 每个排队窗口假设存在100个人
for i in range(100):
lock_obj.acquire()#上锁
if ticket > 0:
print(f'{threading.current_thread().name}正在出售第{ticket}张票')
ticket -= 1
time.sleep(1)
lock_obj.release()#释放锁
if __name__ == '__main__':
for i in range(3): # 创建三个线程,代码三个窗口
t = Thread(target=sale_ticket)
t.start()
'''
线程之间是可以进行数据共享的,需要注意进程之间不会进行共享,但是线程共享数据存在安全性问题
线程安全问题:
使用Lock进行解决
锁定状态:acquire()方法
非锁定状态:release()方法
'''
- 生产者和消费者模式
'''
生产者与消费者模式
生产者与消费者模式:是线程模型中的经典问题,与编程语言无关,当程序中出现了明确的两类任务,一个任务负责生产数据,一个任务负责处理产生的数据时就可以使用该模式
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}取出了{i}数据')
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()
'''
程序:是指一系列有序指令的集合
进程:启动后的程序称为进程,系统会为进程分配内存空间
创建进程语法结构:Process(group=None,target,name,args,kwargs)
进程池:当需要多个进程的时候,就可以使用进程池Pool
创建进程池的语法结构:进程池对象=Pool(N)
并发:是指两个或多个事件同一时刻间隔发生,多个任务被交替轮换着执行
并行:是指两个或多个事件在同一时刻发生,多个任务在同一时刻在多个处理器上同时执行
进程之间不存在共享数据,但可以使用队列进行通信
线程时CPU可调度的最小单位,被包含在进程中,是进程中实际的运作单位,一个进程中可以拥有N多个线程并发执行,每个线程执行不同的任务
创建线程的两种方式:函数式,继承式
同一进程内的多个线程可以共享数据,但是会存在安全性问题
使用Lock锁解决多个线程操作贡献数据的安全问题
生产者和消费者问题是线程模型中的经典问题,与编程语言无关
'''