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)

关于多线程为什么可以共享全局变量,而多进程不可以,个人认为是因为对于多进程来说父进程和子进程拥有不同的进程地址空间,所以得到的全局变量也是不同的,而对于多线程来说各线程共享进程地址空间,拥有相同的全局变量。

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