在python的多線程和多進程中,當我們需要對多線程或多進程的共享資源或對象進行修改操作時,往往會出現因cpu隨機調度而導致結果和我們預期不一致的問題,
線程舉例:
from threading import Thread,Lock
x = 0
def task():
global x
for i in range(200000):
x = x+1
'''
假設
t1 的 x剛拿到0 保存狀態 就被切了
t2 的 x拿到0 進行+1 1
t1 又獲得運行了 x = 0 +1 1
思考:一共加了幾次1? 加了兩次1 真實運算出來的數字本來應該+2 實際只+1
這就產生了數據安全問題.
'''
if __name__ == '__main__':
t1 = Thread(target=task)
t2 = Thread(target=task)
t3 = Thread(target=task)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print(x)
479261
from multiprocessing import Process,Lock
import json,time,os
def search():
time.sleep(1) # 模擬網絡io
with open('db.txt',mode='rt',encoding='utf-8') as f:
res = json.load(f)
print(f'還剩{res["count"]}')
def get():
with open('db.txt',mode='rt',encoding='utf-8') as f:
res = json.load(f)
time.sleep(1) # 模擬網絡io
if res['count'] > 0:
res['count'] -= 1
with open('db.txt',mode='wt',encoding='utf-8') as f:
json.dump(res,f)
print(f'進程{os.getpid()} 搶票成功')
time.sleep(1.5) # 模擬網絡io
else:
print('票已經售空啦!!!!!!!!!!!')
def task():
search()
get()
if __name__ == '__main__':
for i in range(5):
p = Process(target=task)
p.start()
還剩1
還剩1
還剩1
還剩1
還剩1
進程6292 搶票成功
進程10604 搶票成功
進程19280 搶票成功
進程272 搶票成功
進程12272 搶票成功
這時就需要對線程或者進程加鎖,以保證一個線程或進程在對共享對象進行修改時,其他的線程或進程無法訪問這個對象,直至獲取鎖的線程的操作執行完畢后釋放鎖。所以,鎖在多線程和多進程中起到一個同步的作用,以保護每個線程和進程必要操作的完整執行。
#線程鎖
from threading import Thread,Lock
x = 0
mutex = Lock()
def task():
global x
mutex.acquire()
for i in range(200000):
x = x+1
mutex.release()
if __name__ == '__main__':
t1 = Thread(target=task)
t2 = Thread(target=task)
t3 = Thread(target=task)
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print(x)
600000
from multiprocessing import Process,Lock
import json,time,os
def search():
time.sleep(1) # 模擬網絡io
with open('db.txt',mode='rt',encoding='utf-8') as f:
res = json.load(f)
print(f'還剩{res["count"]}')
def get():
with open('db.txt',mode='rt',encoding='utf-8') as f:
res = json.load(f)
# print(f'還剩{res["count"]}')
time.sleep(1) # 模擬網絡io
if res['count'] > 0:
res['count'] -= 1
with open('db.txt',mode='wt',encoding='utf-8') as f:
json.dump(res,f)
print(f'進程{os.getpid()} 搶票成功')
time.sleep(1.5) # 模擬網絡io
else:
print('票已經售空啦!!!!!!!!!!!')
def task(lock):
search()
# 鎖住
lock.acquire()
get()
lock.release()
# 釋放鎖頭
if __name__ == '__main__':
lock = Lock() # 寫在主進程是為了讓子進程拿到同一把鎖.
for i in range(15):
p = Process(target=task,args=(lock,))
p.start()
# p.join()
# 進程鎖 是把鎖住的代碼變成了串行
# join 是把所有的子進程變成了串行
# 為了保證數據的安全,串行犧牲掉效率.
還剩1
還剩1
還剩1
還剩1
還剩1
進程16868 搶票成功
票已經售空啦!!!!!!!!!!!
票已經售空啦!!!!!!!!!!!
票已經售空啦!!!!!!!!!!!
票已經售空啦!!!!!!!!!!!