Python multiprocessing在Windows下遇KeyboardInterrupt无法终止致系统冻结
解决Windows下Python多进程Pool按Ctrl+C锁死的问题
首先,你的代码里有几个关键问题会导致这个锁死现象,再加上Windows系统对多进程信号处理的特殊性,才触发了机器锁死的情况。我来一步步拆解和修复:
1. 先修正代码里的明显错误
你把pool.close()放在了os.walk的循环内部,这意味着第一次遍历完一个目录后,进程池就被关闭了,后续的目录处理会直接报错,而且pool.join()的位置也不对,这会导致资源无法正确释放,是锁死的诱因之一。另外,pool.map()返回的是计算完成的结果列表,不是AsyncResult对象,所以item.wait()完全是无效操作——你等的根本不是异步任务句柄。
2. Windows下Ctrl+C导致锁死的核心原因
在Windows系统中,Python的multiprocessing.Pool的map()方法是完全阻塞的,主进程会一直等待所有子进程完成任务。当你按下Ctrl+C时,主进程收到KeyboardInterrupt,但子进程可能还在执行IO密集型的哈希计算(比如读取大文件),Windows的信号机制无法像Unix那样快速传递终止信号给子进程,导致子进程继续占用CPU和内存资源,甚至和主进程产生资源竞争,最终引发系统锁死。
3. 修复后的代码方案
我们可以改用pool.imap()(或者imap_unordered())来迭代处理结果,这样主进程不会一直阻塞,而是能及时响应中断信号;同时正确管理进程池的生命周期,不在循环内关闭池:
import os import multiprocessing def is_valid_file(file_path): # 你的文件校验逻辑 return os.path.isfile(file_path) def getHashes(file_path): # 你的哈希计算逻辑 import hashlib with open(file_path, 'rb') as f: return hashlib.sha256(f.read()).hexdigest() def syncHashes(rootDir): """ main function """ print(f"Starting the scanner in root {rootDir}") pool = multiprocessing.Pool(processes=10) try: for dirName, subdirList, fileList in os.walk(rootDir): full_path_fnames = map(lambda fn: os.path.join(dirName, fn), fileList) full_path_fnames = filter(is_valid_file, full_path_fnames) # 使用imap迭代处理,让主进程能响应中断 for result in pool.imap(getHashes, full_path_fnames): # 处理每个文件的哈希结果 print(f"Hash: {result}") # 遍历完成后再关闭池 pool.close() pool.join() except KeyboardInterrupt: print("Received interrupt, terminating pool...") pool.terminate() pool.join() if __name__ == "__main__": syncHashes(".") # 替换成你的rootDir
4. 额外优化建议
- 如果你的哈希计算是IO密集型的(读取大量文件),可以考虑减少进程数(比如和CPU核心数一致),避免磁盘IO竞争加剧锁死概率。
- 也可以尝试使用
concurrent.futures.ProcessPoolExecutor,它的中断处理在Windows下更健壮:
from concurrent.futures import ProcessPoolExecutor def syncHashes(rootDir): print(f"Starting the scanner in root {rootDir}") with ProcessPoolExecutor(max_workers=10) as executor: try: for dirName, subdirList, fileList in os.walk(rootDir): full_path_fnames = map(lambda fn: os.path.join(dirName, fn), fileList) full_path_fnames = filter(is_valid_file, full_path_fnames) for result in executor.map(getHashes, full_path_fnames): print(f"Hash: {result}") except KeyboardInterrupt: print("Received interrupt, shutting down executor...") executor.shutdown(wait=False)
这个方案利用了with语句自动管理执行器的生命周期,shutdown(wait=False)会立即终止所有子进程,避免资源占用。
内容的提问来源于stack exchange,提问作者ensnare
相关产品推荐
相关产品推荐

