Потоки Python и GIL

Допустим, у меня есть поток и основная часть программы. Из-за 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()

person Luis Cruz    schedule 17.04.2015    source источник


Ответы (1)


Они будут работать одновременно, но не параллельно. ОС будет часто переключаться между двумя потоками, чтобы они оба могли выполнять свою работу. Это то, что подразумевается под «одновременным»; одному потоку не нужно ждать завершения другого, прежде чем он сможет начать работу. Но из-за 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, например, блокирует ввод-вывод или обращается к библиотеке C, которая освобождает GIL, вы не увидите такого снижения производительности, потому что в этом случае фактически оба потока может работать параллельно. Это неприменимо в приведенном выше примере, поскольку ни один из потоков не выполняет работу, которая освобождает GIL.

person dano    schedule 17.04.2015
comment
Ясно спасибо. Насколько медленнее он будет работать? Это критично? Допустим, я запускаю программу для фондового рынка, один поток получает данные от брокера, а другой исполняет заказы. Задержка должна быть как можно меньше (максимум миллисекунды). Будет ли это работать? - person Luis Cruz; 17.04.2015
comment
@LuisCruz Это несколько зависит от того, что делает каждый поток. В двухпоточной программе, где одна в основном блокирует ввод-вывод, а другая выполняет вычисления, это не должно быть ужасно, но каждый раз при поступлении запроса будут некоторые штрафы. Обычно в Python вы порождает фоновый процесс, использующий multiprocessing для обработки вычислений, если накладные расходы IPC не перевешивают преимущества отказа от GIL. Но если вы хотите, чтобы несколько потоков выполняли вычисления, вам обязательно следует использовать процессы. - person dano; 17.04.2015
comment
Большое спасибо за информацию. В моем случае один поток читает именованный канал, поэтому, если данных нет, он блокируется. Другой поток получает данные от брокера через API веб-сокета. Так что в моем случае потоки не должны быть слишком плохими, я полагаю. - person Luis Cruz; 17.04.2015
comment
@LuisCruz Обычно довольно легко переключаться между потоками и процессами, поэтому вы должны иметь возможность протестировать каждый подход и посмотреть, как это влияет на производительность. - person dano; 17.04.2015
comment
Это лучшее описание тем и проблем с гилом, которое я когда-либо видел. - person Lorenzo Belli; 16.08.2017