6

假设我写了一个 wsgi application。我使用多线程配置运行此应用程序Apache2,以便我的应用程序在每个进程的多个线程中运行:Linuxmod-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. 如果我的网络服务器是,我在 Linux 上的应用程序的每个线程都是使用API 和函数Apache在 C 级创建的,必须执行的是某种 python 解释器的主要函数?还是 Apache 以某种方式保护我免受此错误的影响?pthreadspthread
  3. 如果我在像Tornado's这样的 python 编写的网络服务器上运行它会HTTPServer怎样?用python编写的Web服务器将线程实现为python级threading.Thread对象,并application在每个线程中运行函数。所以,我想这是一个竞争条件?(我还想,在这种情况下,我可以从实现下面的底层 C 级抽象,pthreadsthreading.Thread担心 python 函数,因为解释器不允许我修改 C 级共享数据并搞砸它的功能。所以唯一的方法是对我来说打破线程安全就是处理全局变量?是吗?)
4

2 回答 2

4

是的,你有一个竞争条件,但它与进口无关。中的全局状态foo.a受到 和 之间的数据a + 1竞争a = ...;因为两个线程可以看到相同的值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)

您控制中的任何全局可变状态都需要它。

于 2014-05-15T19:07:55.270 回答
2

阅读关于各种进程/线程配置的 mod_wsgi 文档,尤其是它关于数据共享的内容。

特别是它说:

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

于 2014-05-16T00:38:37.250 回答