Это AFAIK недокументированный, но multiprocessing
имеет класс Finalizer
, "который поддерживает финализацию объекта с использованием слабых ссылок". Вы можете использовать его для регистрации финализатора в вашем initializer
.
Я не вижу multiprocessing.Value
полезного выбора синхронизации в этом случае. Несколько рабочих могут выйти одновременно, сигнализируя, какие целые числа файлов свободны, - это больше, чем может обеспечить (заблокированный) счетчик.
Я бы предложил использовать несколько голых multiprocessing.Lock
с, по одному для каждого файла, вместо:
from multiprocessing import Pool, Lock, current_process
from multiprocessing.util import Finalize
def f(n):
global fileno
for _ in range(int(n)): # xrange for Python 2
pass
return fileno
def init_fileno(file_locks):
for i, lock in enumerate(file_locks):
if lock.acquire(False): # non-blocking attempt
globals()['fileno'] = i
print("{} using fileno: {}".format(current_process().name, i))
Finalize(lock, lock.release, exitpriority=15)
break
if __name__ == '__main__':
n_proc = 3
file_locks = [Lock() for _ in range(n_proc)]
pool = Pool(
n_proc, initializer=init_fileno, initargs=(file_locks,),
maxtasksperchild=2
)
print(pool.map(func=f, iterable=[50e6] * 18))
pool.close()
pool.join()
# all locks should be available if all finalizers did run
assert all(lock.acquire(False) for lock in file_locks)
Вывод:
ForkPoolWorker-1 using fileno: 0
ForkPoolWorker-2 using fileno: 1
ForkPoolWorker-3 using fileno: 2
ForkPoolWorker-4 using fileno: 0
ForkPoolWorker-5 using fileno: 1
ForkPoolWorker-6 using fileno: 2
[0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2]
Process finished with exit code 0
Обратите внимание, что с Python 3 Вы не можете надежно использовать менеджер контекста пула вместо старого способа сделать это, показанного выше. Менеджер контекста пула (к сожалению) вызывает terminate()
, что может привести к остановке рабочих процессов до того, как финализатор сможет запустить.