2009-11-08 16 views
11

Próbuję użyć wątków w projekcie Python, nad którym pracuję, ale wątki nie zachowują się tak jak powinny w moim kodzie. Wygląda na to, że wszystkie wątki są uruchamiane sekwencyjnie (tzn. Wątek 2 rozpoczyna się po zakończeniu wątku 1, obie nie rozpoczynają się w tym samym czasie). Napisałem prosty skrypt, aby przetestować to, i to też uruchamia wątki sekwencyjnie.Python threading wydaje się uruchamiać wątki sekwencyjnie

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() 

Oto wynik dostaję od prowadzenia go:

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

To samo zachowanie obserwuje się znacznie większej liczby iteracji pętli.

Próbowałem przeszukać sieć Web i starsze odpowiedzi SO, ale nie mogłem znaleźć niczego, co pomogłoby. Czy ktoś może wskazać, co jest nie tak z tym kodem?

Odpowiedz

13

Obecnie w pythonie wątki zmieniają się po wykonaniu określonej ilości instrukcji kodu bajtowego. Nie działają w tym samym czasie. Wątki będą wykonywane równolegle tylko wtedy, gdy jeden z nich wywoła moduł intensywnie wykorzystujący I/O lub nie pytony, który może zwolnić GIL (globalna blokada interpretera).

Jestem prawie pewny, że wyniki zostaną pomieszane, jeśli podbijesz liczbę pętli do czegoś podobnego do 10000. Pamiętaj, że samo odradzanie drugiego wątku zajmuje również "dużo" czasu.

+0

To samo zachowanie z 10000 iteracjami – MAK

+0

W rzeczywistym projekcie, nad którym pracuję, jeden z wątków jest nieskończoną pętlą, która nasłuchuje wiadomości i wywołuje funkcję zwrotną jako przybyli. Po prostu blokuje wszystkie pozostałe wątki. Niestety, aktualny kod pętli nie może być modyfikowany (po prostu nazywam metodę run() klasy w wątku). – MAK

+0

Kiedy uruchamiam skrypt w ten sposób: './pythr.py | uniq -c' Otrzymuję: 8969 Hello | 1 Witaj świecie | 6626 świat | 1 | 3373 świat | 1030 Witaj. Zmienia to kontrolę - po prostu nie tak często ... – viraptor

10

W czasie, który zajmuje drugi wątek, rozpoczyna się już pierwsza pętla nitek i odbitki.

Tutaj wygląda to tak, można zobaczyć drugi wątek rozpoczynający się po pierwszym wyemitowanym kilka hellos.

Hello 
Hello 
Hello 
Hello 
Hello 
Helloworld 

Helloworld 

Helloworld 

Helloworld 

Helloworld 

world 
world 
world 
world 
world 

Przy okazji: Twój przykład nie ma żadnego znaczenia. Jedynym powodem wątków jest IO, a IO działa wolno. Po dodaniu jakieś spanie symulować IO powinien działać zgodnie z oczekiwaniami: pojawia

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() 

dzikie mix:

worldHello 

Helloworld 

Helloworld 

worldHello 

Helloworld 

Helloworld 

worldHello 

Helloworld 

worldHello 

Helloworld 
+2

Nie otrzymuję takich wyników, nawet przy znacznie większej/mniejszej liczbie iteracji pętli for. Na moim komputerze jest zawsze sekwencyjny. Myślę, że to zależy od OS/procesora, jak zasugerowałeś. – MAK

+0

Jak powiedziałem w moim pytaniu, jest to tylko przykład mojego problemu, a nie kod, nad którym pracuję (który jest znacznie większy). W moim rzeczywistym kodzie jeden z wątków uruchamia pętlę nasłuchującą sygnałów dbus. – MAK

3

To naprawdę zależy od planującego używanego systemu operacyjnego, za procesora.
Poza tym wiadomo, że wątki CPythona nie są doskonałe z powodu GIL (PDF), co w skrócie oznacza, że ​​wiele wątków działa sekwencyjnie lub coś w tym rodzaju.

+2

Prawdopodobnie oznacza to, że wątki CPython cierpią z powodu GIL ... Nie ma GIL w, powiedzmy, Jython. – EOL

+0

@EOL - masz rację, zaktualizowałem odpowiedź – abyx

4

Zachowanie może się również zmienić w zależności od tego, czy system używa pojedynczego procesora, czy wielu procesorów, zgodnie z wyjaśnieniami podanymi przez this talk przez Davida Beazleya.

Jak mówi viraptor, pierwszy wątek zwolni GIL po wykonaniu sys.getcheckinterval() bytecodes (domyślnie 100). Aby z grubsza podsumować, co mówi David Beazley, w systemie z pojedynczym procesorem druga nitka będzie miała szansę przejąć kontrolę. Jednak w systemie wielordzeniowym drugi wątek może działać na innym rdzeniu, a pierwszy wątek spróbuje ponownie uzyskać dostęp do blokady i prawdopodobnie się powiedzie, ponieważ system operacyjny nie będzie miał czasu na przełączanie procesorów. Oznacza to, że w systemie wielordzeniowym z wątkiem związanym z procesorem, inne wątki mogą nigdy nie zaglądać.

Powoduje to dodanie instrukcji sleep do obu pętli, aby przestały być wyświetlane. Obciążenie procesora.