提问人:vahvero 提问时间:4/18/2023 最后编辑:nigh_anxietyvahvero 更新时间:4/19/2023 访问量:58
为什么删除包含对象引用的集合时不调用“obj.__del__()”
Why is `obj.__del__()` not being called when collection containing object reference is deleted
问:
我正在尝试构建一个子类以在我的桌面中使用多个 GPU。Process
class GPUProcess(mp.Process):
used_ids: list[int] = []
next_id: int = 0
def __init__(self, *, target: Callable[[Any], Any], kwargs: Any):
gpu_id = GPUProcess.next_id
if gpu_id in GPUProcess.used_ids:
raise RuntimeError(
f"Attempt to reserve reserved processor {gpu_id} {self.used_ids=}"
)
GPUProcess.next_id += 1
GPUProcess.used_ids.append(gpu_id)
self._gpu_id = gpu_id
# Define target process func with contant gpu_id
def _target(**_target_kwargs):
target(
**_target_kwargs,
gpu_id=self.gpu_id,
)
super(GPUProcess, self).__init__(target=_target, kwargs=kwargs)
@property
def gpu_id(self):
return self._gpu_id
def __del__(self):
GPUProcess.used_ids.remove(self.gpu_id)
def __repr__(self) -> str:
return f"<{type(self)} gpu_id={self.gpu_id} hash={hash(self)}>"
# Test creation
def test_process_creation():
# Expect two gpus
def dummy_func(*args):
return args
processes = []
for _ in range(2):
p = GPUProcess(
target=dummy_func,
kwargs=dict(a=("a", "b", "c")),
)
processes.append(p)
for p in processes:
p.start()
for p in processes:
p.join()
del processes
assert GPUProcess.used_ids == [], f"{GPUProcess.used_ids=}!=[]"
if __name__ == "__main__":
test_process_creation()
__del__
不为第二个进程调用。
AssertionError: GPUProcess.used_ids=[1]!=[]
为什么不叫第二个?__del__
稍后,我将利用这个类来运行大量有效负载,每个 GPU 使用一个有效负载和一个使用关键字来决定使用设备的函数。这在 Python 中是否明智?mp.Pool
GPUProcess
gpu_id
答:
2赞
nigh_anxiety
4/18/2023
#1
简短的回答是,没有调用,因为前一个 for 循环中的变量仍在引用第二个进程对象__del__
p
object.__del__
根据官方文档,不保证在被调用时被调用。del object
object.__del__
当对象的引用计数为 0 时调用。
关键字将对象的引用计数减少 1。del
因此,您可以通过设置或在调用断言之前删除该引用来解决此问题,或者在退出函数后调用断言,因为这将从该堆栈级别中删除所有引用计数。p = None
del p
test_process_creation()
我发现这个来自 mCoding 频道的视频非常有用,以及如何不使用它。__del__
顺便说一句,我要指出的是,在使用这些更改测试您的代码时,我发现您需要解决几个问题:
- 通过断言后,我遇到了错误。你的方法应该
使用 try/except,或检查self.gpu_id是否在列表中
在删除它之前。
ValueError: list.remove(x): x not in list
__del__()
- 在中,您将虚拟函数定义为 ,但您创建的测试进程专门定义了关键字参数。这将导致异常 。最简单的解决方案是将定义更改为
test_process_creation()
dummy_func(*args)
kwargs=dict(a=("a", "b", "c"))
TypeError: test_process_creation.<locals>.dummy_func() got an unexpected keyword argument 'a'
dummy_func(**kwargs)
评论
0赞
vahvero
4/19/2023
答案是肯定的。我真傻。你对这个问题的一般方法有什么看法吗?
1赞
nigh_anxiety
4/19/2023
@vahvero 跟踪和清理进程 ID 的问题?或者最后一段中提出的关于这是否是一种明智的方法的问题?我没有在 Python 中做过太多的多处理,所以我不能对此发表强烈意见。可能值得作为一个单独的问题来问这种方法是否正确(甚至可能存在一个现有的问题)。我的一个建议是考虑在一个集合而不是列表中跟踪使用过的gpu_ids。由于所有的 gpu 都是整数,因此它们是可散列的,并且从集合中添加和删除元素是 O(1)
1赞
juanpa.arrivillaga
4/19/2023
@vahvero根本不用于此目的,请使用上下文管理器__del__
0赞
vahvero
4/19/2023
我认为这是不可能的,因为该过程必须由国会议员使用。池
评论