2011-03-24 120 views
5

说我有一个写入文件的函数。我也有一个函数循环重复读取文件。我有这两个函数在不同的线程中运行。 (其实我正在通过MDIO读/写寄存器,这就是为什么我不能同时执行两个线程,只有一个或另一个线程,但为了简单起见,我们只是说它是一个文件)Python:线程+锁定会显着减慢我的应用程序的速度

现在当我孤立地运行写函数,它执行相当快。但是,当我运行线程并让它在运行之前获得锁定时,它似乎运行速度非常慢。这是因为第二个线程(读函数)轮询获取锁吗?有什么办法可以解决这个问题吗?

我目前只是使用一个简单的RLock,但是我打开任何可以提高性能的改变。

编辑:作为一个例子,我将举一个发生了什么的基本例子。读线程基本上始终运行,但偶尔会有一个单独的线程发出调用来加载。如果我通过从cmd提示符运行加载来进行基准测试,则在线程中运行速度至少要慢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 
+0

在CPython中,除了释放GIL的C(外部)模块外,一次只能有一个“运行”的线程,因为一次只允许一个线程访问Python引擎。如果MDIO调用没有释放GIL,那么在MDIO调用完成之前,其他函数/ lock *甚至不能启动*(也就是说,Python代码不会运行)。 (我不是线程感知的MDIO。) – 2011-03-24 22:56:58

+0

我用一个例子编辑了这篇文章。你是说,一旦写线程获得锁,读线程永远不会执行?我在印象之下lock.acquire将轮询,直到它获得锁定?还有什么可以放慢上述代码? – 2011-03-24 23:36:26

+0

@Shaunak Amin我没有试图说/暗示:-)但CPython线程不能实际运行多个Python线程(它们在内部锁定在GIL上;只有一些指令允许Python线程“收益“) - 但这并不适用于上述更新,因为这些锁是关于usbmpc访问的。 – 2011-03-25 00:52:28

回答

4

你使用线程多核机器上?

如果答案是肯定的,那么除非您的Python版本是3.2+,否则在运行线程应用程序时将会降低性能。

David Beazly已经付出了相当大的努力来发现GIL在多内核上发生了什么,并且让我们其他人也很容易理解它。检查他的网站和那里的资源。您也可以在PyCon 2010上看到他的presentation。它非常有趣。

简而言之,在Python 3.2中,Antoine Pitrou wrote a new GIL在单核和多核机器上具有相同的性能。在以前的版本中,更多的核心/线程你拥有的性能损耗增加...

希望它能帮助:)

+0

这两个很好的答案,但这些链接给了我很好的继续。我之前已经决定要么删除线程并且自己处理调度(以避免上下文切换伤害性能),因为我的需求相当原始。我希望我可以勾选两个答案。 – 2011-03-26 19:32:54

+2

我自己编写了我自己的调度器来防止GIL在线程之间的上下文切换,而现在花费10分钟以上的函数现在需要20秒(因为删除了几个线程竞争周期)。这是警告:请勿在PYTHON 2.x上使用Threadeding! – 2011-03-28 18:50:01

+0

@ShaunakAmin:该死的。 – 2012-01-11 11:42:41

4

你为什么不收购作家锁为每个时间只写?您目前正在锁定加载函数的整个持续时间,直到加载函数完成后,读者才会进入。

其次,你应该使用上下文锁。您当前的代码不是线程安全的:

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

对于您的读者也是如此。使用上下文来保持锁定。第三,请不要使用RLock。你知道你不需要一个,你的读写代码不需要重新获取,所以不要给它那个机会,你会掩盖错误。

真正的答案出现在你的问题的几条评论中:GIL引发了一些争议(假设它实际上并不是你的误操作)。 Python threading模块非常棒,GIL有时不是,但更重要的是它产生的复杂行为被误解了。值得一提的是,主流认为抛出问题并不是人们相信它的灵丹妙药。它通常不是解决方案。

相关问题