multiprocessing.Lock使用 *** 作系统提供的信号量对象实现。在Linux上,子级只是通过继承了父级的信号量句柄
os.fork。这不是信号量的副本。它实际上继承了父级具有的相同句柄,可以继承文件描述符的相同方式。另一方面,Windows不支持
os.fork,因此必须使Windows处于“腌制”状态
Lock。它
multiprocessing.Lock使用Windows
DuplicateHandleAPI通过创建对象内部使用的Windows信号灯的重复句柄来实现此目的,该API指出:
重复的句柄引用与原始句柄相同的对象。因此,对对象的任何更改都会通过两个手柄反映出来
该
DuplicateHandleAPI允许您将重复句柄的所有权交给子进程,以便子进程在取消选择之后实际上可以使用它。通过创建由孩子拥有的重复句柄,您可以有效地“共享”锁对象。
这是中的信号量对象
multiprocessing/synchronize.py
class SemLock(object): def __init__(self, kind, value, maxvalue): sl = self._semlock = _multiprocessing.SemLock(kind, value, maxvalue) debug('created semlock with handle %s' % sl.handle) self._make_methods() if sys.platform != 'win32': def _after_fork(obj): obj._semlock._after_fork() register_after_fork(self, _after_fork) def _make_methods(self): self.acquire = self._semlock.acquire self.release = self._semlock.release self.__enter__ = self._semlock.__enter__ self.__exit__ = self._semlock.__exit__ def __getstate__(self): # This is called when you try to pickle the `Lock`. assert_spawning(self) sl = self._semlock return (Popen.duplicate_for_child(sl.handle), sl.kind, sl.maxvalue) def __setstate__(self, state): # This is called when unpickling a `Lock` self._semlock = _multiprocessing.SemLock._rebuild(*state) debug('recreated blocker with handle %r' % state[0]) self._make_methods()
请注意
assert_spawningin中的调用
__getstate__,该调用在腌制对象时被调用。这是如何实现的:
## Check that the current thread is spawning a child process#def assert_spawning(self): if not Popen.thread_is_spawning(): raise RuntimeError( '%s objects should only be shared between processes' ' through inheritance' % type(self).__name__ )
该函数可以确保您
Lock通过调用来“继承” the函数
thread_is_spawning。在Linux上,该方法仅返回
False:
@staticmethoddef thread_is_spawning(): return False
这是因为Linux不需要腌制即可继承
Lock,因此,如果
__getstate__实际上是在Linux上调用它,则我们一定不能继承。在Windows上,还有更多 *** 作:
def dump(obj, file, protocol=None): ForkingPickler(file, protocol).dump(obj)class Popen(object): ''' Start a subprocess to run the pre of a process object ''' _tls = thread._local() def __init__(self, process_obj): ... # send information to child prep_data = get_preparation_data(process_obj._name) to_child = os.fdopen(wfd, 'wb') Popen._tls.process_handle = int(hp) try: dump(prep_data, to_child, HIGHEST_PROTOCOL) dump(process_obj, to_child, HIGHEST_PROTOCOL) finally: del Popen._tls.process_handle to_child.close() @staticmethod def thread_is_spawning(): return getattr(Popen._tls, 'process_handle', None) is not None
在这里,
thread_is_spawning返回
True如果
Popen._tls对象具有
process_handle的属性。我们可以看到
process_handle在中创建了属性
__init__,然后使用将要继承的数据从父级传递给子级
dump,然后删除了该属性。所以
thread_is_spawning只会在
True期间
__init__。根据这个python-
ideas邮件列表线程,实际上这是一个人为限制,它被添加来模拟与
os.forkLinux上相同的行为。Windows实际上 可以
支持
Lock随时通过传递,因为
DuplicateHandle可以随时运行。
以上所有内容均适用于该
Queue对象,因为它在
Lock内部使用。
我想说,继承
Lock对象比使用a更可取
Manager.Lock(),因为当使用a时
Manager.Lock,
Lock必须通过IPC将每个对的调用都通过IPC发送到
Manager进程,这比使用
Lock调用内部的共享要慢得多。处理。不过,这两种方法都是完全有效的。
最后,可以使用/关键字参数将a传递给a
Lock的所有成员,
Pool而无需使用a :
Manager``initializer``initargs
lock = Nonedef initialize_lock(l): global lock lock = ldef scenario_1_pool_no_manager(jobfunc, args, ncores): """Runs a pool of processes WITHOUT a Manager for the lock and queue. """ lock = mp.Lock() mypool = mp.Pool(ncores, initializer=initialize_lock, initargs=(lock,)) queue = mp.Queue() iterator = make_iterator(args, queue) mypool.imap(jobfunc, iterator) # Don't pass lock. It has to be used as a global in the child. (This means `jobfunc` would need to be re-written slightly. mypool.close() mypool.join()return read_queue(queue)
之所以可行,是因为传递的参数传递给在中运行的对象
initargs的
__init__方法,因此它们最终被继承而不是被腌制。
Process``Pool
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)