我搜索了一些关于如何正确构建计数器以跟踪完成的工作进度的信息。到目前为止,似乎所有答案都涉及使用lock
和Value
。
我想知道我是否可以使用回调实现它。似乎回调是在主进程中执行的,而不是工作者所在的子进程。我可以进一步假设它是在同一个线程中执行的,因此根本没有竞争条件吗?
import time
import multiprocessing
import os
Pool = multiprocessing.Pool
def sqr(a):
time.sleep(0.5)
print 'local {}'.format(os.getpid())
return a * a
pool = Pool(processes=4)
class Counter(object):
def __init__(self):
self.value = 0
def incr(self, x):
self.value += 1
print 'count {}'.format(self.value)
print 'callback {}'.format(os.getpid())
counter = Counter()
r = [pool.apply_async(sqr, (x,), callback=counter.incr) for x in range(10)]
pool.close()
pool.join()
local 27155local 27154local 27156
count 1
callback 27152
count 2
callback 27152
count 3
callback 27152
local 27153
count 4
callback 27152
local 27155
count 5
callback 27152
local 27156
local 27154
count 6
callback 27152
count 7
callback 27152
local 27153
count 8
callback 27152
local 27155
count 9
callback 27152
local 27156
count 10
callback 27152
main 27152
main count 10
Process finished with exit code 0
好吧,似乎这个link解释了回调背后的一些机制。
所以实际上它在主进程中的不同线程上运行。
但是,我仍然可以用同样的方式实现计数器,因为只有一个线程可以修改计数器吗?
答案 0 :(得分:2)
从@ ami-tavory的注释中的SO链接看,似乎所有回调都可以在同一个线程上调用。但是,由于未在docs或api中指定,因此我不会依赖它,因为它可能在将来或根据实现而改变。
Python没有原子增量(除了一些itertools trick that relies on the GIL),所以为了确保你是线程安全的,你必须使用锁或其他形式的同步。你为什么试图避免它?它可以用作上下文管理器,使代码非常小:
from threading import Lock
class Counter(object):
def __init__(self):
self.value = 0
self.lock = Lock()
def incr(self, x):
with self.lock:
self.value += 1
另一种方法是使用imap_unordered
,在结果可用时(在主线程中)循环结果并在那里更新进度/计数器。
答案 1 :(得分:0)
或者你可以使用imap_unordered作为bj0提到的循环计数:
results = []
for count, result in enumerate(pool.imap_unordered(sqr, range(10)), 1):
results.append(result)
print(count)
就我个人而言,我发现处理imap_unordered()
返回的原始结果比apply_async()
返回的Result对象更直接。