Pythonの高度な特性とネットワーク爬虫類(3):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)

データ競合問題が解決すれば、10つのプロセスが実行され、最後に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が定義できる共有変数のタイプは数値または単一文字またはリストなど様々です.変更後のプログラムは以下のようになり、共有変数はグローバル変数ではなく関数パラメータとしてプロセスに伝達するとともに、その共有変数の操作に対して共有ロックマネージャ()を追加する.ロック()は、データ競合を回避するために:
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)

マルチスレッドはロックによってグローバル変数競合の問題を解決する
マルチスレッド間でグローバル変数を共有できます.データ競合を解決する方法は,共有変数の部分にロックをかけることである(最近,Goプログラム言語設計を学習する第9章:共有変数を用いて同時化を実現する内容において,著者らは,「いわゆる温和なデータ競合は存在しない」という観点を示している.を挙げて、銀行残高を修正した例を2つ挙げて説明します.コンカレントプログラムを書くときは、いわゆる穏やかなデータ競合の考え方を放棄し、マルチプロセスやマルチスレッドデータ競合の出現を避けるべきだと思います).コードは以下の通りです.
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)

マルチスレッドがなぜグローバル変数を共有できるのか、マルチプロセスができないのかについては、個人的には、マルチプロセスにとって親プロセスとサブプロセスが異なるプロセスアドレス空間を持っているため、得られるグローバル変数も異なるが、マルチスレッドにとって各スレッドはプロセスアドレス空間を共有し、同じグローバル変数を持っていると考えられる.