在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
Pool可以提供指定数量的进程,供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。
在这里需要了解阻塞和非阻塞的概念。
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。
阻塞即要等到回调结果出来,在有结果之前,当前进程会被挂起。
Pool的用法有阻塞和非阻塞两种方式。非阻塞即为添加进程后,不一定非要等到改进程执行完就添加其他进程运行,阻塞则相反。
现用一个实例感受一下非阻塞的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from
multiprocessing
import
Lock
,
Pool
import
time
def
function
(
index
)
:
print
'Start process: '
,
index
time
.
sleep
(
3
)
print
'End process'
,
index
if
__name__
==
'__main__'
:
pool
=
Pool
(
processes
=
3
)
for
i
in
xrange
(
4
)
:
pool
.
apply_async
(
function
,
(
i
,
)
)
print
"Started processes"
pool
.
close
(
)
pool
.
join
(
)
print
"Subprocess done."
|
在这里利用了apply_async方法,即非阻塞。
运行结果:
1
2
3
4
5
6
7
8
9
10
|
Started
processes
Start
process
:
Start
process
:
0
1
Start
process
:
2
End
processEnd
process
0
1
Start
process
:
3
End
process
2
End
process
3
Subprocess
done
.
|
可以发现在这里添加三个进程进去后,立马就开始执行,不用非要等到某个进程结束后再添加新的进程进去。
下面再看看阻塞的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from
multiprocessing
import
Lock
,
Pool
import
time
def
function
(
index
)
:
print
'Start process: '
,
index
time
.
sleep
(
3
)
print
'End process'
,
index
if
__name__
==
'__main__'
:
pool
=
Pool
(
processes
=
3
)
for
i
in
xrange
(
4
)
:
pool
.
apply
(
function
,
(
i
,
)
)
print
"Started processes"
pool
.
close
(
)
pool
.
join
(
)
print
"Subprocess done."
|
在这里只需要把apply_async改成apply即可。
运行结果如下:
1
2
3
4
5
6
7
8
9
10
|
Start
process
:
0
End
process
0
Start
process
:
1
End
process
1
Start
process
:
2
End
process
2
Start
process
:
3
End
process
3
Started
processes
Subprocess
done
.
|
这样一来就好理解了吧?
下面对函数进行解释:
apply_async(func[, args[, kwds[, callback]]]) 它是非阻塞,apply(func[, args[, kwds]])是阻塞的。
close() 关闭pool,使其不在接受新的任务。
terminate() 结束工作进程,不在处理未完成的任务。
join() 主进程阻塞,等待子进程的退出, join方法要在close或terminate之后使用。
当然每个进程可以在各自的方法返回一个结果。apply或apply_async方法可以拿到这个结果并进一步进行处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
from
multiprocessing
import
Lock
,
Pool
import
time
def
function
(
index
)
:
print
'Start process: '
,
index
time
.
sleep
(
3
)
print
'End process'
,
index
return
index
if
__name__
==
'__main__'
:
pool
=
Pool
(
processes
=
3
)
for
i
in
xrange
(
4
)
:
result
=
pool
.
apply_async
(
function
,
(
i
,
)
)
print
result
.
get
(
)
print
"Started processes"
pool
.
close
(
)
pool
.
join
(
)
print
"Subprocess done."
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Start
process
:
0
End
process
0
0
Start
process
:
1
End
process
1
1
Start
process
:
2
End
process
2
2
Start
process
:
3
End
process
3
3
Started
processes
Subprocess
done
.
|
另外还有一个非常好用的map方法。
如果你现在有一堆数据要处理,每一项都需要经过一个方法来处理,那么map非常适合。
比如现在你有一个数组,包含了所有的URL,而现在已经有了一个方法用来抓取每个URL内容并解析,那么可以直接在map的第一个参数传入方法名,第二个参数传入URL数组。
现在我们用一个实例来感受一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
from
multiprocessing
import
Pool
import
requests
from
requests
.
exceptions
import
ConnectionError
def
scrape
(
url
)
:
try
:
print
requests
.
get
(
url
)
except
ConnectionError
:
print
'Error Occured '
,
url
finally
:
print
'URL '
,
url
,
' Scraped'
if
__name__
==
'__main__'
:
pool
=
Pool
(
processes
=
3
)
urls
=
[
'https://www.baidu.com'
,
'http://www.meituan.com/'
,
'http://blog.csdn.net/'
,
'http://xxxyxxx.net'
]
pool
.
map
(
scrape
,
urls
)
|
在这里初始化一个Pool,指定进程数为3,如果不指定,那么会自动根据CPU内核来分配进程数。
然后有一个链接列表,map函数可以遍历每个URL,然后对其分别执行scrape方法。
运行结果:
1
2
3
4
5
6
7
8
|
<
Response
[
403
]
>
URL
http
:
//blog.csdn.net/ Scraped
<
Response
[
200
]
>
URL
https
:
//www.baidu.com Scraped
Error
Occured
http
:
//xxxyxxx.net
URL
http
:
//xxxyxxx.net Scraped
<
Response
[
200
]
>
URL
http
:
//www.meituan.com/ Scraped
|
可以看到遍历就这么轻松地实现了。