编程Python 开发者

在 Python 中,传递复合对象(例如字典列表)给线程或进程时会发生什么?在编写与可变集合有关的多线程和多进程应用程序时,重要的是要记住什么?

用 Hintsage AI 助手通过面试

答案。

在 Python 中,在处理线程时,内存中的对象可以无复制地同时被所有线程访问。然而,在通过 multiprocessing 处理进程时,标准的 Python 对象(例如列表、字典)通过序列化(通常使用 pickle)在进程之间传递,每个进程获取对象的副本。在一个进程中更改对象不会反映在另一个进程的副本中。

主要区别:

  • 多线程(threading): 所有线程在共享的地址空间中工作。任何对象(包括嵌套集合)都是共享的。
  • 多进程(multiprocessing): 每个进程都有自己的内存;对象通过序列化传递。进程之间的交换必须使用特殊结构,例如 multiprocessing.Manager().dict()

示例:

# 多进程 from multiprocessing import Process, Manager def worker(d): d['a'] = 42 if __name__ == '__main__': # 普通的 dict 在进程之间不起作用 managed_dict = Manager().dict() p = Process(target=worker, args=(managed_dict,)) p.start(); p.join() print(managed_dict['a']) # 42
  • 如果使用普通的 dict,进程中的更改在主进程中将不可见。

陷阱问题。

在什么情况下,编辑一个进程中的普通列表(list)会反映到另一个进程?

答案:

永远不会,如果你在使用 multiprocessing。普通的 Python 对象不会在进程之间共享。要实现共享,必须使用特殊的原语——管理器对象(例如,Manager().list(),Manager().dict()),它们在进程之间同步状态。

由于对该主题细节不了解而导致的实际错误示例。


故事

一个网络爬虫开发团队将任务队列初始化为列表,并通过 multiprocessing 将其分配给进程。任务“丢失”是因为进程无法看到彼此的更新。他们改为使用 Manager().list()


故事

在一个分析服务中,试图在线程中收集嵌套的 dict 日志。在高负荷场景下,这导致了竞争条件和数据损坏,因为缺乏锁。


故事

在 ETL 服务中,在处理阶段尝试收集的数据对象在多个进程中发现重复/丢失数据:程序员没有考虑到每个进程都在处理自己的结构副本,而不是共享的结构。