Python实用教程_spiritx的博客-CSDN博客
在Python中因为有GIL的原因,线程能提供的并发效果并不理想,Python在多核上如果要释放并发的性能,更多的依靠多进程,我们会比线程更深入的去学习多进程。
进程(Process),顾名思义,就是进行中的程序。有一句话说得好:程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体。进程是资源分配的最小单元,也就是说每个进程都有其单独的内存空间。
Unix/Linux系统通过fork系统调用创建一个进程,但是在Windows中并没有fork调用。但是别担心,Python中内置的multiprocessing模块是跨平台的,我们可以通过对multiprocess模块中的Process类进行实例化创建一个进程对象,如:
import os
from multiprocessing import Process
def run_a_sub_proc(name):
print(f'子进程:{name}({os.getpid()})开始...')
if __name__ == '__main__':
print(f'主进程({os.getpid()})开始...')
# 通过对Process类进行实例化创建一个子进程
p = Process(target=run_a_sub_proc, args=('测试进程', ))
p.start()
p.join()
定义:
class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
进程对象表示在单独进程中运行的活动。
Process 类拥有和 threading.Thread 等价的大部分方法。
在默认情况下,不会将任何参数传递给 target。 args 参数默认值为 (),可被用来指定要传递给 target 的参数列表或元组。
属性和方法名 | 说明 |
name | 进程的名称。该名称是一个字符串,仅用于识别目的。它没有语义。可以为多个进程指定相同的名称。 初始名称由构造器设定。 如果没有为构造器提供显式名称,则会构造一个形式为 'Process-N1:N2:...:Nk' 的名称,其中每个 Nk 是其父亲的第 N 个孩子。 |
run() | Process对象最重要的方法,表示进程活动的方法,可以在子类中重载此方法。 |
start() | 启动进程活动。这个方法每个进程对象最多只能调用一次。它会将对象的 run() 方法安排在一个单独的进程中调用。 |
join([timeout]) | 如果可选参数 timeout 是 None (默认值),则该方法将阻塞,直到调用 join() 方法的进程终止。如果 timeout 是一个正数,它最多会阻塞 timeout 秒。请注意,如果进程终止或方法超时,则该方法返回 None 。检查进程的 exitcode 以确定它是否终止。 一个进程可以被 join 多次。 进程无法join自身,因为这会导致死锁。尝试在启动进程之前join进程是错误的。 |
is_alive() | 返回进程是否还活着。粗略地说,从 start() 方法返回到子进程终止之前,进程对象仍处于活动状态。 |
daemon | 进程的守护标志,一个布尔值。这必须在 start() 被调用之前设置。 初始值继承自创建进程。 当进程退出时,它会尝试终止其所有守护进程子进程。 请注意,不允许在守护进程中创建子进程。这是因为当守护进程由于父进程退出而中断时,其子进程会变成孤儿进程。 另外,这些 不是 Unix 守护进程或服务,它们是正常进程,如果非守护进程已经退出,它们将被终止(并且不被合并)。 |
pid | 返回进程ID。在生成该进程之前,这将是 None 。 |
exitcode | 子进程的退出代码。如果该进程尚未终止则为 None 。 如果子进程的 run() 方法正常返回,退出代码将是 0 。 如果它通过 sys.exit() 终止,并有一个整数参数 N ,退出代码将是 N 。 如果子进程由于在 run() 内的未捕获异常而终止,退出代码将是 1 。 如果它是由信号 N 终止的,退出代码将是负值 -N 。 |
authkey | 进程的身份验证密钥(字节字符串)。 当 multiprocessing 初始化时,主进程使用 os.urandom() 分配一个随机字符串。 当创建 Process 对象时,它将继承其父进程的身份验证密钥,可以通过将 authkey 设置为另一个字节字符串来更改。 |
sentinel | 系统对象的数字句柄 |
terminate() | 终止进程,在UNIX系统中,使用SIGTERM进行终止,在windows系统,调用TerminateProcess进行终止,请注意,进程的后代进程将不会被终止 —— 它们将简单地变成孤立的。 |
kill() | 在UNIX系统中,使用SIGTERM终止进程 |
close() | 关闭 Process 对象,释放与之关联的所有资源。如果底层进程仍在运行,则会引发 ValueError 。一旦 close() 成功返回, Process 对象的大多数其他方法和属性将引发 ValueError 。 |
注意 start() 、 join() 、 is_alive() 、 terminate() 和 exitcode 方法只能由创建进程对象的进程调用。
与线程一样,子进程也有两种创建方式,一种是直接使用process对象来拉起一个对象,另外一种是使用process的子类。
和使用 thread 类创建子线程的方式非常类似,使用 Process 类创建实例化对象,其本质是调用该类的构造方法创建新进程。Process 类的构造方法格式如下:
def __init__(self,group=None,target=None,name=None,args=(),kwargs={})
其中,各个参数的含义为:
import os, time
from multiprocessing import Process
URLS=['https://blog.csdn.net/spiritx/article/details/132783171',
'https://blog.csdn.net/spiritx/article/details/132782806',
'https://blog.csdn.net/spiritx/article/details/132778558',
'https://blog.csdn.net/spiritx/article/details/132773698']
def downloadUrl(url, nsecs):
time.sleep(1)
print(f'进程ID={os.getpid()}, {__name__=} download: {url}')
# p1 = Process(target=downloadUrl, args=(URLS[0], 3)) #RuntimeError
# p1.start()
# downloadUrl(URLS[1], 4)
# p1.join()
if __name__ == '__main__':
p1 = Process(target=downloadUrl, args=(URLS[0], 3))
p2 = Process(target=downloadUrl, args=(URLS[1], 4))
p1.start()
p2.start()
downloadUrl(URLS[3], 4)
p1.join()
p2.join()
‘’'
进程ID=64482, __name__='__main__' download: https://blog.csdn.net/spiritx/article/details/132773698
进程ID=64484, __name__='__mp_main__' download: https://blog.csdn.net/spiritx/article/details/132783171
进程ID=64485, __name__='__mp_main__' download: https://blog.csdn.net/spiritx/article/details/132782806
‘''
注意:因为进程和子进程复用同一套代码,一定要写if __name__ == '__main__':这段代码,否则会出现RuntimeError的异常。
从输出来看,子进程和父进程的__name__并不是一个,父进程是__main__,而子进程是__mp_main__
和使用 thread 子类创建线程的方式类似,除了直接使用 Process 类创建进程,还可以通过创建 Process 的子类来创建进程。
需要注意的是,在创建 Process 的子类时,需在子类重写 run() 方法。实际上,该方法所起到的作用,就如同第一种创建方式中 target 参数执行的函数。
另外,通过 Process 子类创建进程,和使用 Process 类一样,先创建该类的实例对象,然后调用 start() 方法启动该进程。下面程序演示如何通过 Process 子类创建一个进程。
import os, time
import multiprocessing
URLS=['https://blog.csdn.net/spiritx/article/details/132783171',
'https://blog.csdn.net/spiritx/article/details/132782806',
'https://blog.csdn.net/spiritx/article/details/132778558',
'https://blog.csdn.net/spiritx/article/details/132773698']
def downloadUrl(url='', secs=1):
time.sleep(secs)
print(f'进程ID={os.getpid()}, {__name__=} download: {url}')
class MyChildProcess(multiprocessing.Process):
def __init__(self, url, secs):
#super.__init__(self) #TypeError: descriptor '__init__' requires a 'super' object but received a 'MyChildProcess'
multiprocessing.Process.__init__(self)
self.url = url
self.secs = secs
def run(self):
downloadUrl(self.url, self.secs)
if __name__ == '__main__':
p1 = MyChildProcess(URLS[0], 3)
p2 = MyChildProcess(URLS[1], 4)
p1.start()
p2.start()
downloadUrl(URLS[3], 4)
p1.join()
p2.join()
‘’'
进程ID=65688, __name__='__mp_main__' download: https://blog.csdn.net/spiritx/article/details/132783171
进程ID=65686, __name__='__main__' download: https://blog.csdn.net/spiritx/article/details/132773698
进程ID=65689, __name__='__mp_main__' download: https://blog.csdn.net/spiritx/article/details/132782806
‘''
有几个地方要特别注意:
当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到multiprocessing模块提供的Pool方法。
class multiprocessing.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])
初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程他的任务结束,新的任务会交给这个空闲进程去处理。
import os, time
import multiprocessing
import random
URLS=['https://blog.csdn.net/spiritx/article/details/132783171',
'https://blog.csdn.net/spiritx/article/details/132782806',
'https://blog.csdn.net/spiritx/article/details/132778558',
'https://blog.csdn.net/spiritx/article/details/132773698',
'https://blog.csdn.net/spiritx/article/details/5',
'https://blog.csdn.net/spiritx/article/details/6',
'https://blog.csdn.net/spiritx/article/details/7',
'https://blog.csdn.net/spiritx/article/details/8',
'https://blog.csdn.net/spiritx/article/details/9']
INDEXS=[1,2,3,4,5,6,7,8,9]
PARAMS=list(zip(URLS, INDEXS)) #通过zip()方法将多个参数压缩成list对象
def downloadUrl(url, index):
#url, index = param # 对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
time.sleep(random.randint(1,3))
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
results = [pool.apply_async(downloadUrl, param) for param in PARAMS]
pool.close()
pool.join()
for result in results:
print(f'{result.get()=}')
‘’'
进程ID=75676, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/132783171',index=1
进程ID=75677, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/132782806',index=2
进程ID=75675, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/132778558',index=3
进程ID=75678, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/132773698',index=4
进程ID=75677, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/132782806',index=2
进程ID=75677, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=75675, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/132778558',index=3
进程ID=75675, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=75678, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/132773698',index=4
进程ID=75678, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=75676, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/132783171',index=1
进程ID=75676, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=75677, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=75677, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=75676, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=75675, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=75678, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=75677, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
result.get()=1
result.get()=2
result.get()=3
result.get()=4
result.get()=5
result.get()=6
result.get()=7
result.get()=8
result.get()=9
‘''
从上面的结果可以看出,75677进程在处理任务结束后,立即就得到了一个新的任务进行处理。
属性和方法名 | 说明 |
apply(unc[, args=()[, kwds={}]]) | 同步执行,很少用。使用 args 参数以及 kwds 命名参数调用 func , 它会返回结果前阻塞。不建议使用,并且3.x以后不再出现 |
apply_async(func[, args[, kwds[, callback[, error_callback]]]]) | 异步执行。apply() 方法的一个变种,返回一个 AsyncResult 对象。 如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。 如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。 回调函数应该立即执行完成,否则会阻塞负责处理结果的进程。 |
map(func, iterable[, chunksize]) | 内置 map() 函数的并行版本 。 它会保持阻塞直到获得结果。 这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。 注意对于很长的迭代对象,可能消耗很多内存。可以考虑使用 imap() 或 imap_unordered() 并且显式指定 chunksize 以提升效率。 |
map_async(func, iterable[, chunksize[, callback[, error_callback]]]) | map() 方法的一个变种,返回一个 AsyncResult 对象。 如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。 如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。 回调函数应该立即执行完成,否则会阻塞负责处理结果的进程。 |
imap(func, iterable[, chunksize]) | map() 的延迟执行版本。 chunksize 参数的作用和 map() 方法的一样。对于很长的迭代器,给 chunksize 设置一个很大的值会比默认值 1 极大 地加快执行速度。 同样,如果 chunksize 是 1 , 那么 imap() 方法所返回的迭代器的 next() 方法拥有一个可选的 timeout 参数: 如果无法在 timeout 秒内执行得到结果,则 next(timeout) 会抛出 multiprocessing.TimeoutError 异常。 |
imap_unordered(func, iterable[, chunksize]) | 和 imap() 相同,只不过通过迭代器返回的结果是任意的。(当进程池中只有一个工作进程的时候,返回结果的顺序才能认为是"有序"的) |
starmap(func, iterable[, chunksize]) | 和 map() 类似,不过 iterable 中的每一项会被解包再作为函数参数。 比如可迭代对象 [(1,2), (3, 4)] 会转化为等价于 [func(1,2), func(3,4)] 的调用。 |
starmap_async(func, iterable[, chunksize[, callback[, error_callback]]]) | 相当于 starmap() 与 map_async() 的结合,迭代 iterable 的每一项,解包作为 func 的参数并执行,返回用于获取结果的对象。 |
close() | 阻止后续任务提交到进程池,当所有任务执行完成后,工作进程会退出。 |
terminate() | 不必等待未完成的任务,立即停止工作进程。当进程池对象被垃圾回收时,会立即调用 terminate()。 |
join() | 等待工作进程结束。调用 join() 前必须先调用 close() 或者 terminate() |
进程池有多种方式可以提交任务,这里举例说明几种常用的方法。
apply(func [,args [,kwds ] ] )
使用参数args和关键字参数kwds调用func。它会阻塞,直到结果准备就绪。鉴于此块,更适合并行执行工作。此外,func 仅在池中的一个工作程序中执行。
import os
from multiprocessing import Pool
import time
def foo(p):
print(f'进程ID{os.getpid()},{p=}')
time.sleep(0.1)
if __name__=="__main__":
pool = Pool(processes=3)
for i in range(10):
pool.apply(foo, args=(i,))
print('子进程提交完成')
pool.close()
pool.join()
‘’'
进程ID69407,p=0
进程ID69406,p=1
进程ID69409,p=2
进程ID69407,p=3
进程ID69406,p=4
进程ID69409,p=5
进程ID69407,p=6
进程ID69406,p=7
进程ID69409,p=8
进程ID69407,p=9
子进程提交完成
‘''
import os
from multiprocessing import Pool
import time
def foo(p):
print(f'进程ID{os.getpid()} 开始....{p=}')
time.sleep(0.1)
print(f'进程ID{os.getpid()} 结束.{p=}')
if __name__=="__main__":
pool = Pool(processes=3)
for i in range(10):
pool.apply(foo, args=(i,)) #维持执行的进程总数为10,当一个进程执行完后启动一个新进程.
print(f'进程{i}启动...')
print('子进程提交完成')
pool.close()
pool.join()
‘’'
进程ID69541 开始....p=0
进程ID69541 结束.p=0
进程0启动...
进程ID69542 开始....p=1
进程ID69542 结束.p=1
进程1启动...
进程ID69543 开始....p=2
进程ID69543 结束.p=2
进程2启动...
进程ID69541 开始....p=3
进程ID69541 结束.p=3
进程3启动...
进程ID69542 开始....p=4
进程ID69542 结束.p=4
进程4启动...
进程ID69543 开始....p=5
进程ID69543 结束.p=5
进程5启动...
进程ID69541 开始....p=6
进程ID69541 结束.p=6
进程6启动...
进程ID69542 开始....p=7
进程ID69542 结束.p=7
进程7启动...
进程ID69543 开始....p=8
进程ID69543 结束.p=8
进程8启动...
进程ID69541 开始....p=9
进程ID69541 结束.p=9
进程9启动...
子进程提交完成
‘''
从上面的例子可以看到,apply()是顺序启动进程的,在被启动的进程执行完后,再启动下一个进程,实际是没有任何并发执行。
apply_async(func[, args[, kwds[, callback[, error_callback]]]])
异步进程池(非阻塞),返回结果对象的方法的变体。如果指定了回调,则它应该是可调用的,它接受单个参数。当结果变为就绪时,将对其应用回调,即除非调用失败,在这种情况下将应用error_callback。如果指定了error_callback,那么它应该是一个可调用的,它接受一个参数。如果目标函数失败,则使用异常实例调用error_callback。回调应立即完成,否则处理结果的线程将被阻止。
import os
from multiprocessing import Pool
import time
def foo(p):
print(f'进程ID{os.getpid()} 开始....{p=}')
time.sleep(0.1)
print(f'进程ID{os.getpid()} 结束.{p=}')
if __name__=="__main__":
pool = Pool(processes=3)
for i in range(10):
pool.apply_async(foo, args=(i,))
print(f'进程{i}启动...')
print('子进程提交完成')
pool.close()
pool.join()
’’’
进程0启动...
进程1启动...
进程2启动...
进程3启动...
进程4启动...
进程5启动...
进程6启动...
进程7启动...
进程8启动...
进程9启动...
子进程提交完成
进程ID69599 开始....p=0
进程ID69598 开始....p=1
进程ID69600 开始....p=2
进程ID69599 结束.p=0
进程ID69599 开始....p=3
进程ID69598 结束.p=1
进程ID69598 开始....p=4
进程ID69600 结束.p=2
进程ID69600 开始....p=5
进程ID69599 结束.p=3
进程ID69599 开始....p=6
进程ID69598 结束.p=4
进程ID69598 开始....p=7
进程ID69600 结束.p=5
进程ID69600 开始....p=8
进程ID69599 结束.p=6
进程ID69599 开始....p=9
进程ID69598 结束.p=7
进程ID69600 结束.p=8
进程ID69599 结束.p=9
‘’‘
从上面的例子可以看到,apply_async()是异步启动的,子进程还未完全启动,apply_async()就已经返回。
AsyncResult是Pool.apply_async() 和 Pool.map_async() 返回的对象所属的类。
主要方法有
方法 | 说明 |
get([timeout]) | 用于获取执行结果。如果 timeout 不是 None 并且在 timeout 秒内仍然没有执行完得到结果,则抛出 multiprocessing.TimeoutError 异常。如果远程调用发生异常,这个异常会通过 get() 重新抛出。 |
wait([timeout]) | 阻塞,直到返回结果,或者 timeout 秒后超时。 |
ready() | 返回执行状态,是否已经完成。 |
successful() | 判断调用是否已经完成并且未引发异常。 如果还未获得结果则将引发 ValueError。 |
import os, time
import multiprocessing
URLS=['https://blog.csdn.net/spiritx/article/details/132783171',
'https://blog.csdn.net/spiritx/article/details/132782806',
'https://blog.csdn.net/spiritx/article/details/132778558',
'https://blog.csdn.net/spiritx/article/details/132773698']
def downloadUrl(url, nsecs):
time.sleep(1)
print(f'进程ID={os.getpid()}, {__name__=} download: {url}')
return os.getpid()
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
results = [pool.apply_async(downloadUrl, (url, 3)) for url in URLS]
pool.close()
pool.join()
for result in results:
print(f'{result.get()=}')
‘’'
进程ID=72390, __name__='__mp_main__' download: https://blog.csdn.net/spiritx/article/details/132783171
进程ID=72391, __name__='__mp_main__' download: https://blog.csdn.net/spiritx/article/details/132778558
进程ID=72392, __name__='__mp_main__' download: https://blog.csdn.net/spiritx/article/details/132782806
进程ID=72393, __name__='__mp_main__' download: https://blog.csdn.net/spiritx/article/details/132773698
result.get()=72390
result.get()=72392
result.get()=72391
result.get()=72393
‘''
apply()和apply_async()支持多参数的可调用对象,但是在传入参数的时候,需要将多个参数封装为tuple进行传入,apply()和apply_async()会自动解包传给可调用对象。
如下:
import os, time
import multiprocessing
import random
URLS=['https://blog.csdn.net/spiritx/article/details/132783171',
'https://blog.csdn.net/spiritx/article/details/132782806',
'https://blog.csdn.net/spiritx/article/details/132778558',
'https://blog.csdn.net/spiritx/article/details/132773698']
INDEXS=[1,2,3,4,5,6,7,8,9]
PARAMS=list(zip(URLS, INDEXS)) #通过zip()方法将多个参数压缩成list对象
def downloadUrl(url, index): #使用*param也是可以的,需要在函数里解包
#url, index = param # 对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
time.sleep(random.randint(1,3))
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
results = [pool.apply_async(downloadUrl, param) for param in PARAMS]
pool.close()
pool.join()
for result in results:
print(f'{result.get()=}')
’’’
进程ID=72786, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/132783171',index=1
进程ID=72785, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/132782806',index=2
进程ID=72784, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/132778558',index=3
进程ID=72787, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/132773698',index=4
进程ID=72787, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/132773698',index=4
进程ID=72786, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/132783171',index=1
进程ID=72785, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/132782806',index=2
进程ID=72784, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/132778558',index=3
result.get()=1
result.get()=2
result.get()=3
result.get()=4
‘''
注意:apply_async的可调用对象接收的是多个参数。
内置 map() 函数的并行版本 。 它会保持阻塞直到获得结果。
map(func, iterable[, chunksize])
这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。
注意,map()和map_async()的可调用对象不支持多个参数,需要将多参数包装为tuple,或者使用starmap()和starmap_async()。
import os, time
import multiprocessing
import random
URLS=['https://blog.csdn.net/spiritx/article/details/1',
'https://blog.csdn.net/spiritx/article/details/2',
'https://blog.csdn.net/spiritx/article/details/3',
'https://blog.csdn.net/spiritx/article/details/4',
'https://blog.csdn.net/spiritx/article/details/5',
'https://blog.csdn.net/spiritx/article/details/6',
'https://blog.csdn.net/spiritx/article/details/7',
'https://blog.csdn.net/spiritx/article/details/8',
'https://blog.csdn.net/spiritx/article/details/9']
INDEXS=[1,2,3,4,5,6,7,8,9]
PARAMS=list(zip(URLS, INDEXS)) #通过zip()方法将多个参数压缩成list对象
def downloadUrl(param): #不能接受多个参数
url,index = param #对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
time.sleep(random.randint(1,3))
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
pool.map(downloadUrl, PARAMS)
print('pool.map submit ok.')
pool.close()
pool.join()
‘’'
进程ID=72929, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=72930, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=72932, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=72931, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=72929, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=72929, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=72930, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=72930, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=72930, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=72929, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=72930, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=72929, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=72932, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=72932, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=72931, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=72929, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=72930, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=72932, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
pool.map submit ok.
‘''
从上面的输出可以看出,pool.map submit ok是在最后输出的,start download是按顺序输出的,但end download没有按顺序输出,说明:
map_async(func, iterable[, chunksize[, callback[, error_callback]]])
map_async()是map() 方法的一个变种,返回一个 AsyncResult 对象。
如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。
如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。
回调函数应该立即执行完成,否则会阻塞负责处理结果的进程。
import os, time
import multiprocessing
import random
URLS = ['https://blog.csdn.net/spiritx/article/details/1',
'https://blog.csdn.net/spiritx/article/details/2',
'https://blog.csdn.net/spiritx/article/details/3',
'https://blog.csdn.net/spiritx/article/details/4',
'https://blog.csdn.net/spiritx/article/details/5',
'https://blog.csdn.net/spiritx/article/details/6',
'https://blog.csdn.net/spiritx/article/details/7',
'https://blog.csdn.net/spiritx/article/details/8',
'https://blog.csdn.net/spiritx/article/details/9']
INDEXS = [1, 2, 3, 4, 5, 6, 7, 8, 9]
PARAMS = list(zip(URLS, INDEXS)) # 通过zip()方法将多个参数压缩成list对象
def downloadUrl(param): # 只支持一个参数
url, index = param # 对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
time.sleep(random.randint(1, 3))
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
pool.map_async(downloadUrl, PARAMS)
print('pool.map submit ok.')
pool.close()
pool.join()
‘’'
pool.map submit ok.
进程ID=73087, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73088, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73089, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73090, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73087, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73087, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=73088, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73088, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73089, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73089, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73087, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=73087, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73090, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73090, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73087, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73088, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73089, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73090, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
‘''
从上面的例子可以看出,pool.map submit打印在最前面,start download是顺序启动的:
imap(func, iterable[, chunksize])
map() 的延迟执行版本。
chunksize 参数的作用和 map() 方法的一样。对于很长的迭代器,给 chunksize 设置一个很大的值会比默认值 1 极大 地加快执行速度。
同样,如果 chunksize 是 1 , 那么 imap() 方法所返回的迭代器的 next() 方法拥有一个可选的 timeout 参数: 如果无法在 timeout 秒内执行得到结果,则 next(timeout) 会抛出 multiprocessing.TimeoutError 异常。
所谓延迟执行版本,就是并不是所有的子进程都执行完了,才能获得结果,可以在部分进程执行完后,就拿到结果。
下面是map()与imap()拿结果的对比
map版本:
import os, time
import multiprocessing
import random
URLS = ['https://blog.csdn.net/spiritx/article/details/1',
'https://blog.csdn.net/spiritx/article/details/2',
'https://blog.csdn.net/spiritx/article/details/3',
'https://blog.csdn.net/spiritx/article/details/4',
'https://blog.csdn.net/spiritx/article/details/5',
'https://blog.csdn.net/spiritx/article/details/6',
'https://blog.csdn.net/spiritx/article/details/7',
'https://blog.csdn.net/spiritx/article/details/8',
'https://blog.csdn.net/spiritx/article/details/9']
INDEXS = [1, 2, 3, 4, 5, 6, 7, 8, 9]
PARAMS = list(zip(URLS, INDEXS)) # 通过zip()方法将多个参数压缩成list对象
def downloadUrl(param): # 只支持一个参数
url, index = param # 对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
#time.sleep(random.randint(1, 3))
time.sleep(9 - index)
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return 'result:', index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
its = pool.map(downloadUrl, PARAMS)
print('pool.map submit ok.')
for it in its:
print(it)
pool.close()
pool.join()
‘’’
进程ID=73691, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73693, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73690, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73692, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73692, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73692, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=73690, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73690, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73693, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73693, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73691, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73691, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73691, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73691, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73691, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73693, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73690, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73692, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
pool.map submit ok.
('result:', 1)
('result:', 2)
('result:', 3)
('result:', 4)
('result:', 5)
('result:', 6)
('result:', 7)
('result:', 8)
('result:', 9)
’‘’
map()版本的返回值都是要在所有进程都执行完后才能获得。
imap()版本:
import os, time
import multiprocessing
import random
URLS = ['https://blog.csdn.net/spiritx/article/details/1',
'https://blog.csdn.net/spiritx/article/details/2',
'https://blog.csdn.net/spiritx/article/details/3',
'https://blog.csdn.net/spiritx/article/details/4',
'https://blog.csdn.net/spiritx/article/details/5',
'https://blog.csdn.net/spiritx/article/details/6',
'https://blog.csdn.net/spiritx/article/details/7',
'https://blog.csdn.net/spiritx/article/details/8',
'https://blog.csdn.net/spiritx/article/details/9']
INDEXS = [1, 2, 3, 4, 5, 6, 7, 8, 9]
PARAMS = list(zip(URLS, INDEXS)) # 通过zip()方法将多个参数压缩成list对象
def downloadUrl(param): # 只支持一个参数
url, index = param # 对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
#time.sleep(random.randint(1, 3))
time.sleep(9 - index)
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return 'result:', index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
its = pool.imap(downloadUrl, PARAMS)
print('pool.map submit ok.')
for it in its:
print(it)
pool.close()
pool.join()
‘’'
pool.map submit ok.
进程ID=73772, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73773, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73776, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73775, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73775, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73775, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=73776, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73776, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73773, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73773, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73772, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73772, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
('result:', 1)
('result:', 2)
('result:', 3)
('result:', 4)
进程ID=73772, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73772, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73772, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73773, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73776, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73775, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
('result:', 5)
('result:', 6)
('result:', 7)
('result:', 8)
('result:', 9)
‘''
pool.map submit在首行就打印出来,说明imap的子进程与父进程也是并行执行的。
结果数据打印夹杂在子进程输出的结果打印中,是在一项任务执行完后打印出来。但仍可以看出来,结果数据是按任务的进入顺序打印的。(为了演示,我们实际的任务执行时间是,越早提交的任务运行的时间越长。)
imap_unordered(func, iterable[, chunksize])
和 imap() 相同,只不过通过迭代器返回的结果是任意的。(当进程池中只有一个工作进程的时候,返回结果的顺序才能认为是"有序"的)
import os, time
import multiprocessing
import random
URLS = ['https://blog.csdn.net/spiritx/article/details/1',
'https://blog.csdn.net/spiritx/article/details/2',
'https://blog.csdn.net/spiritx/article/details/3',
'https://blog.csdn.net/spiritx/article/details/4',
'https://blog.csdn.net/spiritx/article/details/5',
'https://blog.csdn.net/spiritx/article/details/6',
'https://blog.csdn.net/spiritx/article/details/7',
'https://blog.csdn.net/spiritx/article/details/8',
'https://blog.csdn.net/spiritx/article/details/9']
INDEXS = [1, 2, 3, 4, 5, 6, 7, 8, 9]
PARAMS = list(zip(URLS, INDEXS)) # 通过zip()方法将多个参数压缩成list对象
def downloadUrl(param): # 只支持一个参数
url, index = param # 对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
#time.sleep(random.randint(1, 3))
time.sleep(9 - index)
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return 'result:', index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
its = pool.imap_unordered(downloadUrl, PARAMS)
print('pool.map submit ok.')
for it in its:
print(it)
pool.close()
pool.join()
‘’’
pool.map submit ok.
进程ID=73862, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73864, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73863, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73865, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73865, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73865, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
('result:', 4)
进程ID=73863, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73863, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
('result:', 3)
进程ID=73864, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73864, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
('result:', 2)
进程ID=73862, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73862, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
('result:', 1)
进程ID=73862, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73862, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73862, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
('result:', 8)
('result:', 9)
进程ID=73864, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
('result:', 7)
进程ID=73863, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
('result:', 6)
进程ID=73865, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
('result:', 5)
’‘’
imap_unordered()与imap()几乎一样,只是结果数据是谁先完成谁就能先获得。
map()和map_async()不能支持多参数的可调用对象,starmap()和starmap_async()看上去就像是打的补丁,让他们可以支持多参数的可调用对象。
starmap(func, iterable[, chunksize])
和 map() 类似,不过 iterable 中的每一项会被解包再作为函数参数。
比如可迭代对象 [(1,2), (3, 4)] 会转化为等价于 [func(1,2), func(3,4)] 的调用。
import os, time
import multiprocessing
import random
URLS=['https://blog.csdn.net/spiritx/article/details/1',
'https://blog.csdn.net/spiritx/article/details/2',
'https://blog.csdn.net/spiritx/article/details/3',
'https://blog.csdn.net/spiritx/article/details/4',
'https://blog.csdn.net/spiritx/article/details/5',
'https://blog.csdn.net/spiritx/article/details/6',
'https://blog.csdn.net/spiritx/article/details/7',
'https://blog.csdn.net/spiritx/article/details/8',
'https://blog.csdn.net/spiritx/article/details/9']
INDEXS=[1,2,3,4,5,6,7,8,9]
PARAMS=list(zip(URLS, INDEXS)) #通过zip()方法将多个参数压缩成list对象
def downloadUrl(url, index): #可以支持多个参数
#url,index = param #对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
time.sleep(random.randint(1,3))
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
pool.starmap(downloadUrl, PARAMS)
print('pool.map submit ok.')
pool.close()
pool.join()
‘’'
进程ID=73960, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73962, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73961, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73963, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73960, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73960, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=73962, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73962, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73961, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73961, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73960, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=73960, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73963, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73963, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73962, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73961, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73963, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73960, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
pool.map submit ok.
‘''
starmap_async(func, iterable[, chunksize[, callback[, error_callback]]])
相当于 starmap() 与 map_async() 的结合,迭代 iterable 的每一项,解包作为 func 的参数并执行,返回用于获取结果的对象。
import os, time
import multiprocessing
import random
URLS = ['https://blog.csdn.net/spiritx/article/details/1',
'https://blog.csdn.net/spiritx/article/details/2',
'https://blog.csdn.net/spiritx/article/details/3',
'https://blog.csdn.net/spiritx/article/details/4',
'https://blog.csdn.net/spiritx/article/details/5',
'https://blog.csdn.net/spiritx/article/details/6',
'https://blog.csdn.net/spiritx/article/details/7',
'https://blog.csdn.net/spiritx/article/details/8',
'https://blog.csdn.net/spiritx/article/details/9']
INDEXS = [1, 2, 3, 4, 5, 6, 7, 8, 9]
PARAMS = list(zip(URLS, INDEXS)) # 通过zip()方法将多个参数压缩成list对象
def downloadUrl(url, index): # 只支持一个参数
#url, index = param # 对多个参数进行解包
print(f'进程ID={os.getpid()}, {__name__=} start download: {url=},{index=}')
time.sleep(random.randint(1, 3))
print(f'进程ID={os.getpid()}, {__name__=} end download: {url=},{index=}')
return index
if __name__ == '__main__':
pool = multiprocessing.Pool(4)
pool.starmap_async(downloadUrl, PARAMS)
print('pool.map submit ok.')
pool.close()
pool.join()
‘’'
pool.map submit ok.
进程ID=73991, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73990, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73993, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73992, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73993, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/3',index=3
进程ID=73993, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=73992, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/4',index=4
进程ID=73992, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73991, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/1',index=1
进程ID=73991, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73990, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/2',index=2
进程ID=73990, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73993, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/5',index=5
进程ID=73993, __name__='__mp_main__' start download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
进程ID=73991, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/7',index=7
进程ID=73992, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/6',index=6
进程ID=73990, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/8',index=8
进程ID=73993, __name__='__mp_main__' end download: url='https://blog.csdn.net/spiritx/article/details/9',index=9
‘''