2011-06-18 5 views
5

Przeczytałem wiele przykładów na temat blokowania wątków .. ale dlaczego warto je blokować? Z mojego rozumienia, kiedy rozpoczynasz wątki bez dołączania do nich, rywalizują one z głównym wątkiem i wszystkimi innymi wątkami dla zasobów, a następnie wykonują, czasami jednocześnie, czasami nie.Dlaczego warto blokować wątki?

Czy blokowanie zapewnia, że ​​wątki NIE będą wykonywane jednocześnie?

Co jest złego w równoczesnym wykonywaniu wątków? Czy to nie jest jeszcze lepsze? (szybsza ogólna realizacja)

Kiedy blokujesz wątki, czy zablokuje je wszystkie, czy możesz wybrać te, które chcesz zablokować? (Cokolwiek blokowania faktycznie robi ...)

mam na myśli używając funkcji blokady jak blokady() i nabyć w module gwintowania btw ...

+0

Nie Nie pytam o GIL, znam ograniczenia w python i jestem z niego zadowolony, pytanie dotyczy blokowania wątków z nabywać() i release() niezwiązanych z GIL (inne niż to ma blokadę w nazwie) – MistahX

+1

Retagged, jako nie wyłączny dla 'pythona' w ogóle. –

+0

retagged jako python, mam na myśli metody blokowania w module wątków, zapomniałem dodać, że w – MistahX

Odpowiedz

11

Blokada pozwala wymusić dostęp do wielu wątków zasób jeden na raz, a nie wszystkie próbują jednocześnie uzyskać dostęp do zasobu.

Jak sam zauważysz, zwykle chcesz, aby wątki były wykonywane jednocześnie. Jednak wyobraź sobie, że masz dwa wątki i oboje zapisują do tego samego pliku. Jeśli spróbują napisać do tego samego pliku w tym samym czasie, ich wynik zostanie zamieniony i żaden wątek nie będzie w stanie umieścić w pliku tego, co chciał.

Być może ten problem nie pojawi się cały czas. W większości przypadków wątki nie będą próbowały pisać do pliku naraz. Ale czasami, może raz na tysiąc biegaczy, robią to. Więc może masz błąd, który zdaje się być przypadkowy i trudny do odtworzenia, a przez to trudny do naprawienia. Ugh!

A może ... i stało się to w firmie, w której pracuję ... masz takie błędy, ale nie wiesz, że tam są, ponieważ prawie żaden z twoich klientów nie ma więcej niż 4 procesorów. Wtedy wszyscy zaczynają kupować pudełka 16-procesorowe ... a twoje oprogramowanie działa tak samo jak wiele wątków, ponieważ są rdzenie procesora, więc teraz jest 4 razy więcej wątków i nagle często się załamujesz lub otrzymujesz złe wyniki.

Tak czy inaczej, z powrotem do pliku. Aby zapobiec nawarstwianiu się wątków, każdy wątek musi uzyskać blokadę pliku przed zapisaniem. Tylko jeden wątek może przechowywać blokadę naraz, więc tylko jeden wątek może zapisywać do pliku naraz. Wątek utrzymuje blokadę, dopóki nie zostanie zapisany w pliku, a następnie zwalnia blokadę, aby inny wątek mógł użyć tego pliku.

Jeśli wątki są zapisywane do różnych plików, ten problem nigdy nie powstaje. To jedno rozwiązanie: poproś swoje wątki o zapisanie do różnych plików i połącz je później, jeśli to konieczne. Ale nie zawsze jest to możliwe; czasami jest tylko jeden z czegoś.

To nie muszą być pliki. Załóżmy, że próbujesz po prostu policzyć liczbę wystąpień litery "A" w grupie różnych plików, po jednym wątku na plik. Myślisz, cóż, oczywiście, po prostu wszystkie wątki zwiększą tę samą lokalizację pamięci za każdym razem, gdy zobaczą "A." Ale! Gdy zwiększysz zmienną, która utrzymuje liczbę, komputer odczytuje zmienną do rejestru, zwiększa rejestr, a następnie zapisuje wartość z powrotem. Co się stanie, jeśli dwa wątki odczytują wartość w tym samym czasie, zwiększają ją jednocześnie i zapisują w tym samym czasie? Zaczynają od, powiedzmy, 10, zwiększ ją do 11, zapisz 11 z powrotem. Tak więc licznik powinien mieć 11, kiedy powinno być 12: straciłeś jedną liczbę.

Zdobywanie zamków może być kosztowne, ponieważ trzeba czekać, aż ktoś inny użyje zasobu. Właśnie dlatego globalna blokada interpretera Pythona jest wąskim gardłem wydajności.Dlatego możesz w ogóle unikać korzystania z udostępnionych zasobów. Zamiast używać pojedynczego miejsca w pamięci do przechowywania liczby "A" w twoich plikach, każdy wątek zachowuje swoją własną liczbę i dodajesz je wszystkie na końcu (podobnie do rozwiązania, które zasugerowałem z plikami, wystarczająco zabawnie) .

+0

OK, to ma sens, ale zamki mówię o wątkach blokujących, a nie plikach? Co więc robią? – MistahX

+1

@MistahX: Nie, blokują cokolwiek, zdecydujesz, że się zablokują. Używasz ich tak, jak chcesz, aby wiele wątków nie robiło tego samego w tym samym czasie. Są prymitywami, budujesz z nich to, czego potrzebujesz. Jeśli otoczysz dostęp do pliku w zamku, skutecznie "zablokujesz" ten plik. –

+1

To blokowanie zasobu, a nie bieżący wątek. – Alan

9

Po pierwsze, zamki zostały zaprojektowane w celu ochrony zasobów; wątki nie są "zablokowane" ani "odblokowane" one/nabywają/blokują (w zasobie) i/release/a lock (w zasobie).

Masz rację, że chcesz wątki uruchomić jednocześnie jak najwięcej, ale rzućmy okiem na to:

y=10 

def doStuff(x): 
    global y 
    a = 2 * y 
    b = y/5 
    y = a + b + x 
    print y 

t1 = threading.Thread(target=doStuff, args=(8,)) 
t2 = threading.Thread(target=doStuff, args=(8,)) 
t1.start() 
t2.start() 
t1.join() 
t2.join() 

Teraz możesz wiedzieć, że jeden z tych wątków mogłaby wypełnić i druk pierwszy . Można się spodziewać, że zarówno wyjście 30.

Ale mogą nie.

y jest udostępnionym zasobem, w tym przypadku bity, które czytają i piszą na y, są częścią tak zwanej "sekcji krytycznej" i powinny być chronione przez blokadę. Powodem jest brak jednostek pracy: każdy wątek może uzyskać CPU w dowolnym momencie.

Pomyśl o tym tak:

t1 jest szczęśliwie wykonywania kodu i natrafi

a = 2 * y 

Teraz ma t1 = 20 i zatrzymuje wykonywanie przez chwilę. t2 staje się aktywne, podczas gdy t1 czeka na więcej czasu procesora. T2 instrukcji:

a = 2 * y 
b = y/5 
y = a + b + x 

w tym momencie zmienna globalna y = 30

T2 zatrzymuje wyłączy się na kawałku T1 odbiera ponownie. to wykonuje:

b = y/5 
y = a + b + x 

Ponieważ y było 30, gdy został ustawiony b, b = 6 a y jest teraz ustawiony na 34.

kolejność wydruków niedeterministyczne jak również, a może masz 30 pierwsze lub 34 pierwsze.

stosując blokadę musielibyśmy:

global l 
l = threading.Lock() 
def doStuff(x): 
    global y 
    global l 
    l.acquire() 
    a = 2 * y 
    b = y/5 
    y = a + b + x 
    print y 
    l.release() 

To niekoniecznie sprawia, że ​​ten fragment kodu liniowego - tylko jeden wątek na raz. Ale jeśli cały twój program jest sekwencyjny, to i tak nie powinieneś używać wątków. Chodzi o to, że zyskujesz na przyspieszeniu w oparciu o procentowy udział kodu, który możesz wykonać poza blokadami i działać równolegle. Jest to (jeden powód), dlaczego używanie wątków w systemie dwurdzeniowym nie podwaja wydajności dla wszystkiego.

sama blokada jest również zasobem udostępnionym, ale musi być: po zdobyciu blokady przez jeden wątek wszystkie pozostałe wątki próbujące uzyskać dostęp do tego samego/blokady zostaną zablokowane, dopóki nie zostaną zwolnione. Po zwolnieniu, pierwszy wątek, który porusza się do przodu i pozyska blokadę, zablokuje wszystkie pozostałe wątki oczekujące.

Mamy nadzieję, że to wystarczy, aby kontynuować!