多進程之間通信的限制
- 進程之間是獨立的,互不干擾的內存空間。
我們先看個例子
a = 1 #定義全局變量
def func():
global a
a=2 #修改全局變量值
print(a)
func()
print(a)
運行結果:
image.png
再看利用進程運行的例子:
import multiprocessing
a = 1 #定義全局變量
def func():
global a
a=2 #修改全局變量值
print(a)
process = multiprocessing.Process(target=func)
process.start()
process.join() #等待子進程執(zhí)行完再繼續(xù)執(zhí)行
print(a)
image.png
通過上面2個例子運行結果分析:
按通常應該都是2,應該修改了全局變量值,但是這里只有子進程是2,主進程是1。
這是因為進程之間是獨立的,互不干擾的內存空間,故子進程修改的,不影響主進程的。
進程間通信的解決方案
image.png
print('--------------進程間通信的解決方案--------------')
manager = multiprocessing.Manager() #創(chuàng)建一個服務器進程,并返回與其通信的管理器
list_proxy = manager.list() #通過管理器在服務器進程中開辟一個列表空間,并返回一個代理
print(list_proxy) #用法和list一樣
def func2(list):
list.append('a')
print(list)
#把代理傳給子進程,子進程里就可以通過這個代理,來操作共享空間來進行通信
process2 = multiprocessing.Process(target=func2, args=(list_proxy,))
process2.start()
process2.join() #等待子進程執(zhí)行完再繼續(xù)執(zhí)行
print(list_proxy)
運行結果:image.png
- 一般常用的空間類型是:
mgr.list()、mgr.dict()、mgr.Queue()
多線程之間通信的限制
注意:因為線程屬于同一個進程,因此它們之間共享內存區(qū)域,因此全局變量是公共的。
import threading
a = 1
def func3():
global a
a = 2
print(a)
thread = threading.Thread(target=func3)
thread.start()
thread.join()
print(a)
運行結果:image.png
但是多線程間共享內存間存在競爭問題。
print('--------------多線程共享內存間存在競爭問題--------------')
import threading
data = 0
n = 100000
def add(n):
global data
for i in range(n):
data +=i
def sub(n):
global data
for i in range(n):
data -=i
t_add = threading.Thread(target=add, args=(n,))
t_sub = threading.Thread(target=sub, args=(n,))
t_add.start()
t_sub.start()
t_add.join()
t_sub.join() #這2個地方加join阻塞目的是為了讓子進程執(zhí)行完,最后能在主進程看到data,所以用join來阻塞
print(data)
image.png
加了n次減了n次,結果卻為負數(shù),按正常應該為0。
使用鎖來控制共享資源的訪問。
print('--------------使用鎖來控制共享資源的訪問--------------')
import threading
data = 0
n = 1000000
lock = threading.Lock() #生成一把鎖
def add(n):
global data
for i in range(n):
# lock.acquire() #加鎖
# data +=i
# lock.release() #釋放鎖
#可以寫生上下文格式
with lock:
data +=i
def sub(n):
global data
for i in range(n):
# lock.acquire() #加鎖
# data -=i
# lock.release() #釋放鎖
with lock:
data -=i
t_add = threading.Thread(target=add, args=(n,))
t_sub = threading.Thread(target=sub, args=(n,))
t_add.start()
t_sub.start()
t_add.join()
t_sub.join() #這2個地方加join阻塞目的是為了讓子進程執(zhí)行完,最后能在主進程看到data,所以用join來阻塞
print(data)
運行結果:image.png
這樣才達到目的,就像去銀行存錢取錢,存取不多不少!
線程與進程的安全隊列
隊列:先進先出,一個入口,一個出口。image.png
- 線程安全隊列操作
queue.Queue:
入隊: put(item)
出隊: get()
測試空: empty() # 近似
測試滿: full() # 近似
隊列長度: qsize() # 近似
任務結束: task_done()
等待完成: join() - 進程安全隊列操作
mgr.Queue:
入隊: put(item)
出隊: get()
測試空: empty() # 近似
測試滿: full() # 近似
隊列長度: qsize() # 近似
進程比線程少了task_done()和 join()方法。
生產者和消費者模型
所謂,生產者與消費者模型,本質上是把進程通信的問題分開考慮生產只需要往隊列里面丟東西(生產者不需要關心消費者)消費者,只需要從隊列里面拿東西(消費者也不需要關心生產者)。
image.png
image.png
線程實現(xiàn)生產者-消費者模型
print('--------------生產者與消費者模型--------------')
'''
所謂,生產者與消費者模型,本質上是把進程通信的問題分開考慮
生產者,只需要往隊列里面丟東西(生產者不需要關心消費者)
消費者,只需要從隊列里面拿東西(消費者也不需要關心生產者)
'''
print('--------------多線程的消費者與生產者模式--------------')
'''
生產者:沒滿,則生產,只關心隊列是否已滿。滿了就阻塞。
消費者:只關心隊列是否為空。不為空,則消費,為空則阻塞。
'''
import threading
import queue
import random
import time
class Producer(threading.Thread): #生產者
def __init__(self, queue):
super().__init__()
self.queue = queue
def run(self):
while True:
item = random.randint(0, 10) #創(chuàng)建0~99
#只要隊列沒滿,就向隊列中添加數(shù)據(jù)
self.queue.put(item)
print('生產者-->生產:%s'%item)
time.sleep(1)
class Customer(threading.Thread):
def __init__(self, queue):
super().__init__()
self.queue = queue
def run(self):
while True:
#只要隊列不為空,就從隊列中取數(shù)據(jù)
itme = self.queue.get()
print('消費者-->消費:%s'%itme)
time.sleep(1)
q =queue.Queue(5) #長度為5
producer = Producer(q)
custormer = Customer(q)
producer.start()
custormer.start()
producer.join()
運行結果:image.png
進程實現(xiàn)生產者-消費者模型
import multiprocessing
import queue
import random
import time
class Producer(multiprocessing.Process): #生產者
def __init__(self, queue):
super().__init__()
self.queue = queue
def run(self):
while True:
item = random.randint(0, 10) #創(chuàng)建0~99
#只要隊列沒滿,就向隊列中添加數(shù)據(jù)
self.queue.put(item)
print('生產者-->生產:%s'%item)
time.sleep(1)
class Customer(multiprocessing.Process):
def __init__(self, queue):
super().__init__()
self.queue = queue
def run(self):
while True:
#只要隊列不為空,就從隊列中取數(shù)據(jù)
itme = self.queue.get()
print('消費者-->消費:%s'%itme)
time.sleep(1)
manager = multiprocessing.Manager() #創(chuàng)建一個服務器進程,并返回與其通信的管理器
q =manager.Queue(5) #長度為5
producer = Producer(q)
custormer = Customer(q)
producer.start()
custormer.start()
producer.join()
運行結果:image.png