Python高級特性與網絡爬蟲(三):Python多進程與多線程解決數據競爭的方法

多進程通過multiprocess.Manager()解決共享變量的問題

之前寫多進程爬取微博用戶圖片的時候https://blog.csdn.net/weixin_41977332/article/details/105591034,出現一個問題,一開始想定義一個全局變量cnt_pic來計量下載了多少圖片,每個進程利用multiprocessing庫中提供的Lock()來解決數據競爭的問題,結果發現無法解決,還是出現了全局變量修改混亂的問題,爲了方便講述,把出問題的程序簡化爲如下邏輯:

from multiprocessing import Pool,freeze_support,Lock
import time
cnt=0
lock=Lock()
def test():
	global cnt
	global lock
	with lock:
        time.sleep(0.5)
        cnt=cnt+1
if __name__=='__main__':
    freeze_support()
    start=time.time()
    pool=Pool()
    pool.map(test,range(0,100))
    pool.close()
    pool.join()
    print(cnt)
    print(time.time()-start)

如果數據競爭問題解決的話,執行了十個進程,最後cnt的結果應該是10,結果每次print的結果都是0…,然後通過閱讀這篇博文https://www.cnblogs.com/lsdb/p/10815319.html瞭解到了多進程共享變量的方法是不能通過全局變量來進行的(上個程序中最後print的cnt應該是主進程中的cnt,子進程中修改的cnt和主進程中的cnt應該是不一樣的?),可以通過multiprocessing.Manager()模塊來實現進程間變量的共享。Manager()返回的manager對象控制了一個server進程,此進程包含的python對象可以被其他的進程通過proxies來訪問。從而達到多進程間數據通信且安全。Manager支持的類型有list,dict,Namespace,Lock,RLock,Semaphore,BoundedSemaphore,Condition,Event,Queue,Value和Array,即Manager可以定義的共享變量類型可以是數值或者單個字符或者列表等多種類型。經過修改後的程序如下所示,共享變量應該作爲一個函數參數而不是全局變量來傳入進程當中,同時針對該共享變量的操作應該加一個共享鎖Manager().Lock()來避免數據競爭:

from multiprocessing import Pool,freeze_support,Lock,Process,Manager
import time

##多進程共享變量
def test(cnt,lock):
	time.sleep(0.5)
	with lock:
        cnt.value+=1
if __name__=='__main__':
    freeze_support()
    start=time.time()
    cnt=Manager().Value('i',0)
    lock=Manager().Lock()
    process_list=[]
    for i in range(100):
        process_1=Process(target=test,args=(cnt,lock))
        process_list.append(process_1)
    for process in process_list:
    	process.start()
    for process in process_list:
        process.join()
    print(cnt.value)
    print(time.time()-start)

多線程通過Lock來解決全局變量競爭的問題

多線程之間可以共享全局變量,解決數據競爭的方法就是針對共享變量的部分要加鎖(最近在學習Go程序語言設計的第9章:使用共享變量實現併發的內容中,作者提出一個觀點:“不存在所謂的溫和的數據競態”,並列舉了兩個協程修改銀行餘額的例子來說明,我認爲大家在寫併發程序的時候都應該放棄有所謂的溫和的數據競態的想法,避免多進程或多線程數據競態的出現),代碼如下所示:

import threading
import time
##多線程共享變量
cnt=0
lock=threading.Lock()
def test():
    global cnt
    global lock
    with lock:
        for i in range(1000000):
            cnt+=1

start=time.time()
thread_list=[]
for i in range(10):
    thread=threading.Thread(target=test)
    thread_list.append(thread)
for thread in thread_list:
    thread.start()
for thread in thread_list:
    thread.join()
print(cnt)
print(time.time()-start)

關於多線程爲什麼可以共享全局變量,而多進程不可以,個人認爲是因爲對於多進程來說父進程和子進程擁有不同的進程地址空間,所以得到的全局變量也是不同的,而對於多線程來說各線程共享進程地址空間,擁有相同的全局變量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章