吐槽
作为一名小白,初次写的爬虫,对于性能、耗时等等完全不在意。但是之前写的验证100个proxyIp的有效性
话的时间是在无法接受470秒。在被逼无奈的情况下,用多进程改进,但是途中闹了很多笑话,这里记录一下。
背景
模块:
1. multiprocessing.Pool (PS:就在网上查了一下用进程池的比较多。这里就介绍一下简单的用法,详细介绍请自行查文档)
def func(param):
return param
pool = Pool(4) #参数可选,不填默认cpu的核数。
for i in range(10):
pool.apply_async(func, args=(param,))#异步开启子进程
pool.close() #pool必须先关闭
pool.join() #等待全部进程结束
错误示范
- 自以为是的认为进程池中的进程可以共享list对象变量。
def valid_proxy(proxy, proxy_list):
#验证有效性
proxy_list.append(proxy)
pool = Pool(8)
for proxyIp in proxy_list:
pool.apply_async(valid_proxy, args=(proxy, proxy_list))
pool.close()
pool.join()
PS:刚开始把进程相关的全还给老师啦,以为是python函数的传递参数的问题。
结论:**普通的全局变量不能被子进程所共享**
2. 对python函数参数传递的错误理解。(这个很突兀,主要是我在写代码的随便冒出来的)
这里简单的总结记录:python中分变量和对象。
1. 对象分为不可变对象(number, string, tuple),可变对象(list, dict)
2. 变量保存的是对象的地址。
**结论:python函数参数传递传的是变量所保存的对象的地址。**
PS:
1. 用type(var)判断变量保存的对象的类型
2. 用id(var)查看变量保存的对象的地址。
这里给个例子解释一下为什么我想当然认为进程可以共享变量
from multiprocessing import Pool
def print_id(proxy, proxy_list):
print(type(proxy_list))
print(id(proxy_list))
proxy_list.append(proxy)
if __name__ == '__main__':
proxy_list = [] #全局变量(并不是真正意义上的)
pool = Pool(4)
for i in range(10):
pool.apply_async(print_id, args=(i, proxy_list))
pool.close()
pool.join()
print(proxy_list)
这里id(list)的输出同是同一个地址,我想当然的觉得可以这么写。但是print(list)却是[]空的,在这里
联系一下linux中的fork的原理,fork会将复制父进程的内存空间。加上虚拟地址概念,
导致这里id(list)输出都是同一个地址。(纯属瞎猜,有大神了解详情请指教。)所以得出了上述结论。
正确展示(自以为的)
经过上述的错误经历,差不多快回忆起,好像还有一个共享内存的概念了。之后就查了和Pool相关的Manager。这里简单介绍一下multiprocessing.Manager可以创建适用Pool的共享变量。
from multiprocessing import Pool, Lock, Manager def print_id(proxy, proxy_list): print(type(proxy_list)) print(id(proxy_list)) proxy_list.append(proxy) if __name__ == '__main__': pool = Pool(4) proxy_list = Manager().list() for i in range(10): pool.apply_async(print_id, args=(i, proxy_list)) pool.close() pool.join() print(proxy_list)
PS:申明一下小白,在这里并没有苛刻性能,因为这个在windows下,多进程只能在main函数中, 所以就会涉及到将Manager对象传递给实例化的子进程。但是这个过程会消耗巨大资源,性能比较差。 而且大家都说在使用多进程的过程中,最好不要使用共享资源。这个版本是我之后写的。
在发现传值行不通的情况下,我果断的选择了保存每个进程的返回值。代码如下
from multiprocessing import Pool
def print_id(proxy):
return proxy
if __name__ == '__main__':
pool = Pool(4)
proxy_list = []
for i in range(10):
proxy_list.append(pool.apply_async(print_id, args=(i,)))
pool.close()
pool.join()
for i in proxy_list:
print(i.get())#这里需要注意get函数必须在进程运行完毕才能使用
PS:其实也没有太过较真这两种方案的性能。以本人这种浅薄的功力,实在太过无奈。