2014-09-11 40 views
8

我在网络服务器(apache + modwsgi + django)上使用pandas,并且有一个难以复制的bug,现在我发现它是由不是线程安全的熊猫引起的。熊猫和numpy线程安全

经过大量的代码缩减后,我终于找到了一个简短的独立程序,可以用来重现问题。你可以在下面看到它。

重点是:与this question的回答相反,这个例子表明即使非常简单的操作不会修改数据帧,熊猫也会崩溃。我无法想象这个简单的代码片断如何可能与线程不安全...

问题是关于在网络服务器中使用熊猫和numpy。可能吗?我应该如何使用熊猫来修复我的代码? (锁的使用的一个例子将是有益的)

这里是使段错误的代码:

import threading 
import pandas as pd 
import numpy as np 

def let_crash(crash=True): 
    t = 0.02 * np.arange(100000) # ok con 10000                    
    data = pd.DataFrame({'t': t}) 
    if crash: 
     data['t'] * 1.5 # CRASH 
    else: 
     data['t'].values * 1.5 # THIS IS OK! 

if __name__ == '__main__': 
     threads = [] 
     for i in range(100): 
      if True: # asynchronous                       
       t = threading.Thread(target=let_crash, args =()) 
       t.daemon = True 
       t.start() 
       threads.append(t) 
      else: # synchronous                        
       let_crash() 
     for t in threads: 
      t.join() 

我的环境:蟒2.7.3,1.8.0 numpy的,熊猫0.13.1

+1

不会为我崩溃。 Python 2.7.6,numpy 1.8.2,熊猫0.14.1。我尝试了主循环,直到“10000”。 – osa 2014-09-24 04:22:50

回答

3

请参阅此处的文档中的警告:http://pandas.pydata.org/pandas-docs/dev/gotchas.html#thread-safety

熊猫不是线程安全的,因为底层的复制机制不是。 Numpy我相信有一个原子复制操作,但熊猫有一个层以上。

复制是大熊猫操作的基础(如大多数操作产生一个新的对象返回给用户)

这是不平凡的解决这一问题,并会拿出一个相当沉重的PERF的成本因此将需要一点点正确处理这一问题的工作。

最简单的就是不要在线程之间共享对象或将它们锁定在使用状态。

+1

但是没有任何对象被共享,他的DataFrame对于每个线程来说都是本地的......这看起来和[this]非常相似(https://github.com/numpy/numpy/issues/4642),并且解决方案应该是关于释放GIL更加小心,请参阅[这里](https://github.com/numpy/numpy/pull/4648)。你确定你没有在需要调用Python API函数的地方发布GIL吗? – Jaime 2014-09-11 11:20:52

+0

nope - 这些都涉及numpy/Numexpr(这里没有涉及c或cython代码)所以可能会出现问题 – Jeff 2014-09-11 12:11:21

+0

实际上,它们涉及一些cython代码,但在列访问中。数据['t']''用于各种类型的检查/索引。应该是线程安全的。 – Jeff 2014-09-11 12:23:35

0

将mod_wsgi配置为在单线程模式下运行。

WSGIDaemonProcess mysite processes=5 threads=1 
WSGIProcessGroup mysite 
WSGIApplicationGroup %{GLOBAL} 

在这种情况下,它使用的mod_wsgi daemon模式,这样的进程/线程可以独立于你使用任何的Apache MPM进行设置。

+0

我已经试过这个解决方案,但是线程数= 1会让一些请求需要很长时间才能挂起。 – 2014-09-12 19:57:09

+1

进程在这种情况下提供了并发性,为什么你有多个进程。您实际指定了多少个进程?请求的平均运行时间是多少?吞吐量是多少?您需要了解这些才能正确提供足够的容量。如果只有特定的URL在多线程中遇到了这个问题,那么您可以在多个mod_wsgi守护进程组中将应用程序垂直分区,并将不安全的URL保存在单线程进程中。看到下面的帖子:http://blog.dscpl.com.au/2014/02/vertically-partitioning-python-web.html – 2014-09-12 20:24:01

+0

这很有趣。我确信threads = 1是不对的......我不知道在哪里寻找进程的数量,我有apache的默认配置。但是pstree告诉我有很多(超过100)。问题在于文件上传...当客户端启动文件上传(可能需要几分钟)时,服务器变得无法响应。我认为与postgresql事务有关联,并试图使用手动提交等类似的东西,但没有成功。 – 2014-09-13 06:08:25