Потоковая обработка Python, по-видимому, последовательно запускает потоки

Я пытаюсь использовать потоки в проекте Python, над которым я работаю, но потоки, похоже, не ведут себя так, как они должны быть в моем коде. Кажется, что все потоки выполняются последовательно (т.е. Thread2 начинается после завершения потока 1, они не запускаются одновременно). Я написал простой script, чтобы проверить это, и это тоже запускает потоки последовательно.

import threading

def something():
    for i in xrange(10):
        print "Hello"

def my_thing():
    for i in xrange(10):
        print "world"   

threading.Thread(target=something).start()
threading.Thread(target=my_thing).start() 

Здесь вывод, который я получаю от его запуска:

Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
world
world
world
world
world
world
world
world
world
world

Такое же поведение наблюдается при значительно большем числе итераций петель.

Я попробовал поиск в Интернете и более старые ответы SO, но я не мог найти ничего, что помогло. Кто-нибудь может указать, что не так с этим кодом?

Ответ 1

В настоящее время в python потоки изменяются после выполнения определенного количества команд байтового кода. Они не работают одновременно. У вас будут только потоки, выполняемые параллельно, когда один из них вызывает некоторый модуль с интенсивным использованием ввода-вывода или не работает с python, который может освободить GIL (глобальный блокиратор интерпретатора).

Я уверен, что вы получите смешанный результат, если вы нажмете число циклов на что-то вроде 10000. Помните, что просто нерестование второго потока также занимает много времени.

Ответ 2

В то время, когда второй поток запускает первые петли потоков и печатает уже.

Здесь это выглядит так, вы можете видеть второй поток, начинающийся после первого испускания нескольких приветствий.

Hello
Hello
Hello
Hello
Hello
Helloworld

Helloworld

Helloworld

Helloworld

Helloworld

world
world
world
world
world

Btw: Ваш пример не имеет смысла. Единственная причина для потоков - IO, а IO - медленная. Когда вы добавляете спящий режим для имитации ввода-вывода, он должен работать как ожидалось:

import threading
from time import sleep

def something():
    for i in xrange(10):
        sleep(0.01)
        print "Hello"

def my_thing():
    for i in xrange(10):
        sleep(0.01)
        print "world"   

threading.Thread(target=something).start()
threading.Thread(target=my_thing).start()

появляется дикий микс:

worldHello

Helloworld

Helloworld

worldHello

Helloworld

Helloworld

worldHello

Helloworld

worldHello

Helloworld

Ответ 3

Поведение может также изменяться в зависимости от того, использует ли система один процессор или несколько процессоров, что объясняется этот разговор от David Бизли.

Как говорит viraptor, первый поток освободит GIL после выполнения sys.getcheckinterval() bytecodes (по умолчанию 100). Чтобы грубо суммировать то, что говорит Дэвид Безли, на одной процессорной системе второй поток будет иметь шанс взять на себя. Однако в многоядерной системе второй поток может работать на другом ядре, и первый поток попытается восстановить блокировку и, вероятно, будет успешным, поскольку у ОС не будет времени для переключения процессоров. Это означает, что в многоядерной системе с потоком, связанным с процессором, другие потоки могут никогда не смотреть.

Как это сделать, добавьте оператор sleep в оба цикла, чтобы они больше не зависали от ЦП.

Ответ 4

Это действительно зависит от планировщика вашей операционной системы, вашего процессора.
Кроме того, известно, что потоки CPython не идеальны из-за GIL (PDF), что, короче говоря, означает что много раз потоки выполняются последовательно или что-то в этом роде.