Python:线程和锁显著降低了我的应用速度

7 投票
2 回答
6646 浏览
提问于 2025-04-16 14:22

假设我有一个函数可以写入文件,还有一个函数可以不断地从这个文件中读取内容。这两个函数是在不同的线程中运行的。(实际上,我是通过MDIO来读写寄存器,所以这两个线程不能同时执行,只能一个执行,另一个等待。但为了简单起见,我们就假设是在操作文件)

当我单独运行写入函数时,它执行得很快。但是当我在多线程环境下运行,并且在执行之前需要获取一个锁时,它的速度似乎变得非常慢。这是因为第二个线程(读取函数)在不断尝试获取这个锁吗?有没有什么办法可以解决这个问题?

我现在使用的是一个简单的可重入锁(RLock),但我愿意尝试任何可以提高性能的方案。

补充说明:举个例子,我会简单描述一下发生了什么。读取线程基本上是一直在运行的,但偶尔会有一个单独的线程调用加载功能。如果我通过命令提示符运行加载功能进行基准测试,线程运行的速度至少慢了3倍。

写入线程:

import usbmpc # functions I made which access dll functions for hardware, etc

def load(self, lock):
    lock.acquire()
    f = open('file.txt','r')
    data = f.readlines()
    for x in data: 
        usbmpc.write(x)
    lock.release()

读取线程:

import usbmpc

def read(self, lock): 
    addr = START_ADDR
    while True: 
        lock.acquire()
        data = usbmpc.read(addr)
        lock.release()
        addr += 4
        if addr > BUF_SIZE: addr = START_ADDR

2 个回答

4

你为什么不在写入的时候只锁定写入的那段时间呢?你现在是把整个加载函数的时间都锁住了,这样读者就得等到加载函数完全完成才能进来。

其次,你应该使用上下文锁。你现在的代码并不安全,可能会出现问题:

def load(lock):
    for x in data:
        with lock:
            whatever.write(x)

你的读者也是一样,应该用上下文来保持锁定。

第三,不要使用RLock。你知道其实不需要这个锁,因为在你的读写代码中没有必要重新获取锁,所以别给它这个机会,这样只会掩盖潜在的错误。

真正的原因在于你问题下面的一些评论:全局解释器锁(GIL)导致了一些竞争(假设不是你对锁的误用)。Python的threading模块非常棒,但GIL有时候就不那么好了,尤其是它产生的一些复杂行为常常被误解。不过值得一提的是,大家普遍认为用线程来解决问题是个好办法,但其实这并不是人们想象中的灵丹妙药,通常并不是解决方案。

4

你在多核机器上使用线程吗?

如果答案是“是”,那么除非你的Python版本是3.2或更高,否则在运行多线程应用时,你的性能会受到影响。

David Beazly 花了很多时间研究多核机器上的GIL(全局解释器锁)问题,并且把这些内容整理得很容易理解。你可以去他的网站看看那里的资源。此外,你可能还想看看他在2010年PyCon上的演讲,内容相当有趣。

简单来说,在Python 3.2中,Antoine Pitrou 写了一个新的GIL,使得在单核和多核机器上的性能表现相同。而在之前的版本中,核心或线程越多,性能损失就越大……

希望这对你有帮助 :)

撰写回答