独立与非独立的内存空间
同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源相互独立。
我们看一个简单的例子,使用多个线程/进程向同一个list
内添加值。
多线程的代码这样写:
from threading import Thread, Lock
import time
def func(nums, i, lock: Lock):
lock.acquire() # 打印控制台是共享资源,需要请求锁
print(f"Thread -{i} : ", id(nums))
lock.release() # 释放锁
for x in range(3):
time.sleep(1)
lock.acquire() # nums也是共享资源,需要请求锁
nums.append(i)
print(f"Thread -{i} : ", nums)
lock.release() # 释放锁
if __name__ == '__main__':
lock = Lock()
nums = []
print("Main Thread :", id(nums))
t_list = []
for i in range(3):
t = Thread(target=func, args=(nums, i, lock, ))
t_list.append(t)
t.start()
for t in t_list:
t.join()
print("Main Thread :", nums)
输出的结果是我们想象中的结果,多个线程写入的是同一个list
:
Main Thread : 2112122344008
Thread -0 : 2112122344008
Thread -1 : 2112122344008
Thread -2 : 2112122344008
Thread -0 : [0]
Thread -1 : [0, 1]
Thread -2 : [0, 1, 2]
Thread -1 : [0, 1, 2, 1]
Thread -0 : [0, 1, 2, 1, 0]
Thread -2 : [0, 1, 2, 1, 0, 2]
Thread -0 : [0, 1, 2, 1, 0, 2, 0]
Thread -1 : [0, 1, 2, 1, 0, 2, 0, 1]
Thread -2 : [0, 1, 2, 1, 0, 2, 0, 1, 2]
Main Thread : [0, 1, 2, 1, 0, 2, 0, 1, 2]
那么我们换成多进程呢?代码稍微修改一下:
from multiprocessing import Process, Lock
import time
def func(nums, i, lock: Lock):
lock.acquire() # 打印控制台是共享资源,需要请求锁
print(f"Process -{i} : ", id(nums))
lock.release() # 释放锁
for x in range(3):
time.sleep(1)
lock.acquire() # nums也是共享资源,需要请求锁
nums.append(i)
print(f"Process -{i} : ", nums)
lock.release() # 释放锁
if __name__ == '__main__':
lock = Lock()
nums = [] # 一个普通的list变量
print("Main Process :", id(nums))
p_list = []
for i in range(3):
p = Process(target=func, args=(nums, i, lock, ))
p_list.append(p)
p.start()
for p in p_list:
p.join()
print("Main Process :", nums)
结果输出,和多线程是不同的,和我们想象中的效果不一样啊:
Main Process : 2153732646280
Process -0 : 2598231611336
Process -1 : 2290033891400
Process -2 : 2314883635464
Process -0 : [0]
Process -1 : [1]
Process -2 : [2]
Process -0 : [0, 0]
Process -1 : [1, 1]
Process -2 : [2, 2]
Process -0 : [0, 0, 0]
Process -1 : [1, 1, 1]
Process -2 : [2, 2, 2]
Main Process : []
每个进程都具有独立的内存空间。我们使用创建子进程的时候,子进程Process替我们深度复制了传入的参数,生成了自己的进程空间。因此不同进程操作的list
并不是同一个list
,而是各个子进程复制出来的list
。如何在多进程中共享数据呢?这我们就要使用Python的中的管理器了。
进程管理器——不同进程中共享数据
multiprocessing.Manager
管理器提供了一种创建共享数据的方法,从而可以在不同进程中共享,甚至可以通过网络跨机器共享数据。管理器维护一个用于管理 共享对象 的服务。其他进程可以通过代理访问这些共享对象。
from multiprocessing import Process, Lock, Manager
import time
def func(nums, i, lock: Lock):
lock.acquire() # 打印控制台是共享资源,需要请求锁
print(f"Process -{i} : ", id(nums))
lock.release() # 释放锁
for x in range(3):
time.sleep(1)
lock.acquire() # nums也是共享资源,需要请求锁
nums.append(i)
print(f"Process -{i} : ", nums)
lock.release() # 释放锁
if __name__ == '__main__':
lock = Lock()
nums = Manager().list([]) # 多个进程需要使用manager来传递共享变量
print("Main Process :", id(nums))
p_list = []
for i in range(3):
p = Process(target=func, args=(nums, i, lock, ))
p_list.append(p)
p.start()
for p in p_list:
p.join()
print("Main Process :", nums)
输出结果,即便各个进程打印出来list
的id不同,但是仍然是同一数据,这就是管理器的作用:
Main Process : 2511697914568
Process -2 : 2023818640072
Process -1 : 1574718126600
Process -0 : 2238624938312
Process -2 : [2]
Process -1 : [2, 1]
Process -0 : [2, 1, 0]
Process -2 : [2, 1, 0, 2]
Process -1 : [2, 1, 0, 2, 1]
Process -0 : [2, 1, 0, 2, 1, 0]
Process -2 : [2, 1, 0, 2, 1, 0, 2]
Process -1 : [2, 1, 0, 2, 1, 0, 2, 1]
Process -0 : [2, 1, 0, 2, 1, 0, 2, 1, 0]
Main Process : [2, 1, 0, 2, 1, 0, 2, 1, 0]