在Python中的线程之间共享dicts时,是否可以避免锁定开销?

时间:2015-09-18 17:30:18

标签: python multithreading immutability

我在Python中有一个多线程应用程序,其中线程读取非常大(因此我无法将它们复制到线程本地存储)dicts(从磁盘读取并且从未修改过)。然后他们使用dicts作为只读数据处理大量数据:

# single threaded
d1,d2,d3 = read_dictionaries()
while line in stdin:
    stdout.write(compute(line,d1,d2,d3)+line)

我试图通过使用线程来加快速度,然后每个线程都会读取自己的输入并编写自己的输出,但由于这些线程很大,我希望线程共享存储。

IIUC,每次线程从dict读取时,它都必须将其锁定,这会对应用程序造成性能损失。这种数据锁定不是必需的,因为dicts是只读的。

CPython是否实际上单独锁定数据,还是只使用GIL

如果确实存在per-dict锁定,有没有办法避免它?

1 个答案:

答案 0 :(得分:3)

python中的多线程处理是没用的。最好使用多处理模块。因为多线程只能在较少数量的情况下给予积极的努力。

  

Python实现细节:在CPython中,由于Global   解释器锁,只有一个线程可以一次执行Python代码   (即使某些面向性能的库可能会克服   这个限制)。如果您希望您的应用程序更好地使用   建议您使用多核机器的计算资源   使用多处理。但是,线程仍然是一个合适的模型   如果要同时运行多个I / O绑定任务。   Official documentation

在没有任何代码示例的情况下,我只建议将您的大字典拆分为多个部分,并使用Pool.map处理每个部分。并将结果合并到主要过程中。

不幸的是,不可能在不同的python进程之间共享大量内存(我们不是在讨论基于mmap的共享内存模式)。但是您可以在不同的过程中阅读字典的不同部分。或者只是在主进程中读取整个字典,并为子进程提供一个小块。

另外,我应该警告你,你应该非常谨慎地使用多处理算法。因为每个额外的兆字节将乘以进程数。

因此,基于您的伪代码示例,我可以假设两种基于compute函数的可能算法:

# "Stateless"
for line in stdin:
    res = compute_1(line) + compute_2(line) + compute_3(line)
    print res, line

# "Shared" state
for line in stdin:
    res = compute_1(line)
    res = compute_2(line, res)
    res = compute_3(line, res)
    print res, line

在第一种情况下,您可以创建多个工作人员,根据Process class在单独的工作人员中读取每个字典(最好减少每个进程的内存使用量),并将其计算为生产线。

在第二种情况下,您有一个共享状态。对于每个下一个工作人员,您需要前一个工作的结果。这是多线程/多处理编程的最坏情况。但你可以编写算法,有几个工人正在使用相同的队列并将结果推送到它而无需等待所有周期的完成。您只需在流程之间共享Queue实例。