Python线程和GIL

6

假设我有一个线程和程序的主要部分。由于GIL的存在,一次只能有一个线程在工作(而不是同时)对吗?但是如果其中一个线程是无限循环(或两个都是),会怎么样?

这两个进程会并行运行吗?

def test():
    while True:
       print "hello"

def test2():
    while True:
       print "hi"

def start_thread():
    try: 
        thread.start_new_thread( test2,() )
    except:
        print "Error: Unable to start thread"
start_thread()
test()
1个回答

6
它们将同时运行,但不会实际上是“并行”的。操作系统会频繁地在两个线程之间切换,以便它们都能完成工作。这就是所谓的“并发”;一个线程不需要等待另一个线程完成才能开始工作。但由于GIL的存在,它们永远不会真正同时运行,在不同的核心上并行运行。每个线程将运行一段时间,暂停一下,让另一个线程运行,然后再开始运行,然后暂停,依此类推。
如果您只运行示例代码,很容易看到这一点。以下是我的机器上的输出:
hello
hi
hello
hi
hello
hi
hello
hi
hello
hi
hello
hi
hello
hi
hello
hi
hello
hi

显然,两个线程都在运行。每个线程的运行速度比程序中只有一个线程运行时慢。

考虑这个例子,其中每个线程计算斐波那契数列:

import thread
import time


def fib(n):
    if n <= 1:
        return n

    return fib(n-1) + fib(n-2)

def test():
    while True:
       start = time.time()
       out = fib(32)
       print "hello %s: %s" % (out, str(time.time() - start))

def test2():
    while True:
       out = fib(20)

def start_thread():
    try: 
        thread.start_new_thread( test2,() )
    except:
        print "Error: Unable to start thread"
#start_thread()
test()

仅运行test(没有第二个线程)时,我得到以下输出:

hello 2178309: 0.953778982162
hello 2178309: 0.954975128174
hello 2178309: 0.95578789711
hello 2178309: 0.949182033539

如果我也将test2在后台启动,那么会得到如下结果:
hello 2178309: 4.07990288734
hello 2178309: 4.08523893356
hello 2178309: 2.51651597023
hello 2178309: 2.13291287422
hello 2178309: 2.19885015488

正如你所看到的,性能受到了巨大的影响。

请注意,如果其中一个线程正在执行释放GIL的操作 - 比如阻塞I/O或调用释放GIL的C库 - 你将不会看到这种性能下降,因为在这种情况下,两个线程实际上可以并行运行。但是,在上面的示例中,由于两个线程都没有执行释放GIL的工作,因此不适用该规则。


我明白了,谢谢。它会变慢多少?这很关键吗?假设我正在运行一个股票市场的程序,一个线程正在从经纪人那里接收数据,另一个线程正在执行订单。延迟应该尽可能低(最多几毫秒)。这仍然有效吗? - Luis Cruz
@LuisCruz 这要看每个线程在做什么。在一个双线程程序中,其中一个线程大部分时间都在阻塞 I/O,而另一个线程正在进行计算,这不应该是可怕的,但每当请求到来时都会有一些惩罚。通常在 Python 中,您可以使用 multiprocessing 生成后台进程来处理计算,只要 IPC 开销不超过避免 GIL 的好处。但如果您想让多个线程进行计算,那么绝对应该使用进程。 - dano
非常感谢提供的信息。在我的情况下,一个线程正在读取命名管道,因此如果没有数据,它会被阻塞。另一个线程通过websocket API从代理接收数据。所以在我的情况下,线程应该不会太糟糕。 - Luis Cruz
@LuisCruz 通常在线程和进程之间切换相当容易,因此您应该能够测试每种方法并查看性能受到的影响。 - dano
这是我见过的关于线程和GIL问题最好的描述。 - Lorenzo Belli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接