多线程Web服务器上的python + wsgi:这是竞争条件吗?

时间:2014-05-15 18:06:46

标签: python django multithreading wsgi gil

假设我写了一个wsgi application。我在Apache2 Linux上使用多线程mod-wsgi配置运行此应用程序,以便我的应用程序在每个进程的多个线程中运行:

WSGIDaemonProcess mysite processes=3 threads=2 display-name=mod_wsgi
WSGIProcessGroup mysite
WSGIScriptAlias / /some/path/wsgi.py

应用程序代码是:

def application(environ, start_response):
    from foo import racer
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [racer()] #call to racer creates a race condition?

模块foo.py:

a = 1
def racer():
    global a
    a = a + 1
    return str(a)

我是否仅使用变量a创建竞争条件?我想,a是一个模块级变量,它存在于foo.py中并且在线程中是相同的(共享的)?

从中得出更多理论问题:

  1. 同一进程中的并发线程访问并修改相同的a变量,所以我的示例不是线程安全的?
  2. 如果我的网络服务器是Apache,那么Linux上我的应用程序的每个线程都是在C级别上使用pthreads API创建的,而pthread必须执行的功能是那种python解释器的主要功能?或者Apache是​​否以某种方式保护我免受此错误的影响?
  3. 如果我在像Tornado的{​​{1}}这样的python编写的网络服务器上运行该怎么办?用python编写的Web服务器将线程实现为python级HTTPServer对象,并在每个线程中运行threading.Thread函数。那么,我想这是一场竞争条件? (我也想,在这种情况下,我可以从application实现下面的底层C级pthreads中抽象出来,并且只关心python函数,因为解释器不允许我修改C级共享数据因此,打破线程安全的唯一方法就是处理全局变量?是吗?)

2 个答案:

答案 0 :(得分:4)

是的,那里有竞争条件,但它与进口无关。 foo.a中的全球状态受a + 1a = ...之间的数据竞争影响;因为两个线程可以看到a的相同值,因此计算相同的后继。

导入机制本身通过进程范围锁定来防止多个线程的重复导入(请参阅imp.lock_held())。虽然理论上这可能导致死锁,但这几乎不会发生,因为很少有python模块在导入时锁定其他资源。

这也表明随意修改sys.path可能是安全的;因为这通常仅在导入时发生(为了进行额外的导入),并且因此线程已经拥有导入锁,其他线程也不会导致也会修改该状态的导入。

racer()中修复比赛非常简单:

import threading
a = 1
a_lock = threading.Lock()

def racer():
    global a
    with a_lock:
        my_a = a = a + 1
    return str(my_a)

这将是您控件中任何全局可变状态所需的。

答案 1 :(得分:2)

阅读有关各种进程/线程配置的mod_wsgi文档,特别是有关数据共享的内容。

特别说:

  

如果仍然使用子进程本地模块中的全局数据,   例如,作为缓存,访问和修改全局数据   必须受到本地线程锁定机制的保护。