Python基础语法文章导航:
- Python基础(01初识数据类型&变量)
- Python基础(02条件&循环语句)
- Python基础(03字符串格式化&运算符&进制&编码)
- Python基础(04 基础练习题)
- Python数据类型(day05整型&布尔类型&字符串类型)
- Python数据类型(06列表&元组)
- Python数据类型(07集合&字典&浮点型&None)
- Python文件操作01(自动化测试文件相关操作)
-
Python函数入门(08函数定义&参数&返回值)
-
Python文件操作02(自动化测试文件相关操作)
-
Python函数(10生成器&内置函数&推导式)
-
Python函数(11自定义模块&第三方模块&内置模块)
-
Python函数(12时间处理&正则表达式)
-
Python函数(13面向对象)
-
Python面向对象(15成员&成员修饰符)
-
Python函数(16进程和线程)
-
Python基础语法(17多线程&线程锁&单例模式)
目录
一.多进程开发
1. 进程介绍
2. 常见功能
(1)p.start()
(2)p.join()
(3)p.daemon = 布尔值
(4)进程的名称的设置和获取
(5)自定义进程类,直接将线程需要做的事写到run方法中。
(6)CPU个数,程序一般创建多少个进程?(利用CPU多核优势)
二. 进程间数据的共享
1.共享
2.交换
(1)Queues
(2)Pipes
3. 进程锁
4. 进程池
一.多进程开发
进程是计算机中资源分配的最小单元;一个进程中可以有多个线程,同一个进程中的线程共享资源;进程与进程之间则是相互隔离。
Python中通过多进程可以利用CPU的多核优势,计算密集型操作适用于多进程。
1. 进程介绍
import multiprocessing
def task(arg):
pass
def run():
p1 = multiprocessing.Process(target=task, args=('XXX'), )
#当前进程准备就绪,等待被CPU调度(工作单元其实是进程中的线程)
p1.start()
if __name__ == '__main__':
run()
2. 常见功能
进程的常见方法:
(1)p.start()
当前进程准备就绪,等待被CPU调度(工作单元其实是进程中的线程)。
(2)p.join()
等待当前进程的任务执行完毕后再向下继续执行。
def task(arg):
time.sleep(2)
print("执行中...")
if __name__ == '__main__':
multiprocessing.set_start_method("spawn")
name=[]
p1=multiprocessing.Process(target=task,args=('xxx',))
p1.start()
p1.join()
time.sleep(2)
print("继续执行...")
#执行中...
#继续执行...(过了2秒后出现)
(3)p.daemon = 布尔值
守护进程(必须放在start之前)
-
p.daemon =True
,设置为守护进程,主进程执行完毕后,子进程也自动关闭。 -
p.daemon =False
,设置为非守护进程,主进程等待子进程,子进程执行完毕后,主进程才结束。
import multiprocessing
import time
def task(arg):
time.sleep(2)
print("执行中...")
if __name__ == '__main__':
multiprocessing.set_start_method("spawn")
p1=multiprocessing.Process(target=task,args=('xxx',))
p1.daemon=True
p1.start()
time.sleep(2)
print("继续执行...")
#继续执行...
import multiprocessing
import time
def task(arg):
time.sleep(2)
print("执行中...")
if __name__ == '__main__':
multiprocessing.set_start_method("spawn")
p1=multiprocessing.Process(target=task,args=('xxx',))
p1.daemon=False
p1.start()
time.sleep(2)
print("继续执行...")
# 继续执行...
# 执行中...
(4)进程的名称的设置和获取
-
getpid
:返回调用进程自身的进程ID。用于标识进程自身。 -
getppid
:返回调用进程的父进程ID。用于标识创建调用进程的父进程。 -
multiprocessing.current_process().name:获取当前进程的名字
import multiprocessing
import os
import threading
import time
def func():
time.sleep(3)
def task(arg):
for i in range(10):
t=threading.Thread(target=func)
t.start()
#getpid、getppid分别获取当前进程的进程ID和当前进程的父进程ID。
print(os.getpid(),os.getppid())
print("线程个数",len(threading.enumerate()))
time.sleep(2)
print("当前进程的名称:",multiprocessing.current_process().name)
if __name__ == '__main__':
print(os.getpid())
multiprocessing.set_start_method("spawn")
p1=multiprocessing.Process(target=task,args=('xxx',))
p1.name="哈哈哈哈哈"
p1.start()
print("继续执行...")
# 33756
# 继续执行...
# 22624 33756
# 线程个数 11
# 当前进程的名称: 哈哈哈哈哈
备注:线程计算方法 有1(执行task的线程)+ 10(循环创建的线程)= 11个线程
(5)自定义进程类,直接将线程需要做的事写到run方法中。
import multiprocessing
class MyProcess(multiprocessing.Process):
def run(self):
print("执行此进程",self)
if __name__ == '__main__':
multiprocessing.set_start_method("spawn")
p=MyProcess(args=('xxx',))
p.start()
print("继续执行...")
# 继续执行...
# 执行此进程 <MyProcess name='MyProcess-1' parent=29592 started>
(6)CPU个数,程序一般创建多少个进程?(利用CPU多核优势)
import multiprocessing
n=multiprocessing.cpu_count()
print(n)
#8
import multiprocessing
def task():
pass
if __name__ == '__main__':
count=multiprocessing.cpu_count()
for i in range(count-1):
p=multiprocessing.Process(target=task)
p.start()
二. 进程间数据的共享
进程是资源分配的最小单元,每个进程中都维护自己独立的数据,不共享。
import multiprocessing
def task(data):
data.append(666)
if __name__ == '__main__':
data_list=[]
p=multiprocessing.Process(target=task,args=(data_list,))
p.start()
p.join()
print("主进程:",data_list)
# 主进程: []
如果想要让他们之间进行共享,则可以借助一些特殊的东西来实现。
1.共享
multiprocessing.Value: 这是一个类,用来创建可以在不同进程间共享的值。它允许在多进程环境中安全地共享基本数据类型,如整数、浮点数等。
在 Python 的 multiprocessing 模块中,常见的类型代码包括 'i'(整数)、'd'(浮点数)、'c'(字符,但通常用于 Array 而非 Value)等。
from multiprocessing import Value, Process
def func(n,m1,m2):
n.value=888
#将字符串 'a' 使用 utf-8 编码转换成了字节串 b'a',然后赋值给 m1.value
m1.value='a'.encode('utf-8')
m2.value="武"
if __name__ == '__main__':
#这里的作用是使用Value类从multiprocessing模块创建一个可以在进程间共享的整数值
#'i': 这个字符代表了要创建的共享变量的类型。在本例中,'i' 表示这是一个整数(integer)类型。
# 666: 这是共享变量初始化的值。即这个可以在进程间共享的整数的初始值被设定为666。
num=Value('i',666)
#创建了一个新的共享内存变量 v1,类型指定为 'c',这通常表示一个字符类型
v1=Value('c')
#试图使用 'u' 类型创建一个 multiprocessing.Value 实例。但是,按照 multiprocessing.Value 的标准用法,类型代码 'u' 并不是直接支持的。
v2=Value('u')
p=Process(target=func,args=(num,v1,v2))
p.start()
p.join()
print(num.value)
print(v1.value)
print(v2.value)
# 888
# b'a'
# 武
from multiprocessing import Process, Value, Array
def f(data_array):
data_array[0] = 666
if __name__ == '__main__':
arr = Array('i', [11, 22, 33, 44]) # 数组:元素类型必须是int; 只能是这么几个数据。
p = Process(target=f, args=(arr,))
p.start()
p.join()
print(arr[:])
#[666, 22, 33, 44]
利用 Python 的 multiprocessing 模块中的 Manager 类来创建一个管理器上下文。这个管理器允许您创建可在多个进程之间共享的容器,如列表、字典等。这种方式比直接使用 Value 或 Array 更灵活,尤其适合于需要共享复杂数据结构的场景。
from multiprocessing import Process, Manager
def f(d, l):
d[1] = '1'
d['2'] = 2
d[0.25] = None
l.append(666)
if __name__ == '__main__':
with Manager() as manager:
d = manager.dict()
l = manager.list()
p = Process(target=f, args=(d, l))
p.start()
p.join()
print(d)
print(l)
# {1: '1', '2': 2, 0.25: None}
# [666]
2.交换
(1)Queues
使用 queue = multiprocessing.Queue() 这段代码时,创建一个线程或进程安全的队列,这个队列允许您在多个进程之间安全地传递数据,用于进程间通信的一个非常实用的数据结构。
q.put(i) 表示正向这个队列中放入一个元素 i。这里 i 应该是一个在上下文中已经定义过的变量,意味着即使有多个进程同时尝试向队列中放入或获取数据,队列也能保证数据的正确性和完整性,不会引发冲突。
一旦数据被放入队列,其他进程可以通过 q.get() 方法来取出这个数据。这是一种典型的“生产者-消费者”模型,其中一个进程(生产者)向队列中放入数据,另一个或多个进程(消费者)从队列中取出并处理数据。
import multiprocessing
def task(q):
for i in range(10):
q.put(i)
if __name__ == '__main__':
queue = multiprocessing.Queue()
p = multiprocessing.Process(target=task, args=(queue,))
p.start()
p.join()
print("主进程")
print(queue.get())
print(queue.get())
print(queue.get())
print(queue.get())
print(queue.get())
# 主进程
# 0
# 1
# 2
# 3
# 4
(2)Pipes
import time
import multiprocessing
def task(conn):
time.sleep(1)
conn.send([111, 22, 33, 44])
data = conn.recv() # 阻塞
print("子进程接收:", data)
time.sleep(2)
if __name__ == '__main__':
parent_conn, child_conn = multiprocessing.Pipe()
p = multiprocessing.Process(target=task, args=(child_conn,))
p.start()
info = parent_conn.recv() # 阻塞
print("主进程接收:", info)
parent_conn.send(666)
# 主进程接收: [111, 22, 33, 44]
# 子进程接收: 666
3. 进程锁
如果多个进程抢占式去做某些操作时候,为了防止操作出问题,可以通过进程锁来避免。
lock = multiprocessing.RLock() 时,您初始化了一个可重入锁(Reentrant Lock),这是 multiprocessing 模块提供的一个同步原语。可重入锁允许同一个进程多次获得同一个锁,而不会发生死锁。这对于需要在多进程中保护共享资源的情况非常有用,尤其是当某个进程可能在其持有锁的期间再次请求同一把锁时。
在每个进程中,对于那些需要互斥访问的资源,您需要在访问之前获取锁(lock.acquire()),并在访问完毕后释放锁(lock.release())
import time
import multiprocessing
def task(lock):
print("开始")
lock.acquire()
# 假设文件中保存的内容就是一个值:10
with open('f1.txt', mode='r', encoding='utf-8') as f:
current_num = int(f.read())
print("排队抢票了")
time.sleep(0.5)
current_num -= 1
with open('f1.txt', mode='w', encoding='utf-8') as f:
f.write(str(current_num))
lock.release()
if __name__ == '__main__':
multiprocessing.set_start_method("spawn")
lock = multiprocessing.RLock() # 进程锁
for i in range(10):
p = multiprocessing.Process(target=task, args=(lock,))
p.start()
# spawn模式,需要特殊处理。
time.sleep(7)
# 开始
# 排队抢票了
# 开始
# 开始
# 开始
# 开始
# 开始
# 开始
# 开始
# 开始
# 开始
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
4. 进程池
注意:如果在进程池中要使用进程锁,则需要基于Manager中的Lock和RLock来实现。
通过使用 manager = multiprocessing.Manager() 创建的管理器,您能够创建可在多个进程间共享的同步对象,如锁。因此,lock_object = manager.RLock() # Lock 正确地创建了一个可在进程池中各个进程间共享的可重入锁(RLock)。这解决了之前直接使用 multiprocessing.RLock() 时遇到的共享问题,因为 Manager 自动处理了跨进程的数据同步。
import time
import multiprocessing
from concurrent.futures.process import ProcessPoolExecutor
def task(lock):
print("开始")
# lock.acquire()
# lock.relase()
with lock:
# 假设文件中保存的内容就是一个值:10
with open('f1.txt', mode='r', encoding='utf-8') as f:
current_num = int(f.read())
print("排队抢票了")
time.sleep(1)
current_num -= 1
with open('f1.txt', mode='w', encoding='utf-8') as f:
f.write(str(current_num))
if __name__ == '__main__':
pool = ProcessPoolExecutor()
# lock_object = multiprocessing.RLock() # 不能使用
manager = multiprocessing.Manager()
lock_object = manager.RLock() # Lock
for i in range(10):
pool.submit(task, lock_object)
# 开始
# 排队抢票了
# 开始
# 开始
# 开始
# 开始
# 开始
# 开始
# 开始
# 排队抢票了
# 开始
# 排队抢票了
# 开始
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了
# 排队抢票了