2016-05-25 11 views
7

The documentation dla runtime.LockOsThread stany:Dlaczego Locky nie blokuje tego wątku systemu operacyjnego?

LockOSThread druty wywołujący goroutine do jego aktualnego wątku systemu operacyjnego. Dopóki wywołująca goroutine nie opuści lub nie odwiedzi UnlockOSThread, zawsze będzie wykonywana w tym wątku, a żadna inna goroutine nie będzie mogła.

Ale rozważyć ten program:

package main 

import (
    "fmt" 
    "runtime" 
    "time" 
) 

func main() { 
    runtime.GOMAXPROCS(1) 
    runtime.LockOSThread() 
    go fmt.Println("This shouldn't run") 
    time.Sleep(1 * time.Second) 
} 

main goroutine jest podłączony do jednego dostępnego wątku OS ustalonym przez GOMAXPROCS, więc byłoby oczekiwać, że goroutine utworzona w wierszu 3 main nie będzie działać. Zamiast tego program drukuje This shouldn't run, zatrzymuje się na 1 sekundę i kończy pracę. Dlaczego to się dzieje?

Odpowiedz

6

Z runtime package documentation:

Zmienna GOMAXPROCS ogranicza liczbę wątków systemu operacyjnego, które może wykonać użytkownik poziomu kodu Go jednocześnie. Nie ma ograniczenia liczby wątków, które można blokować w wywołaniach systemowych w imieniu kodu Go; te nie liczą się z limitem GOMAXPROCS.

Śpiący wątek nie wlicza się do wartości GOMAXPROCS 1, więc Go jest wolny mieć inny wątek uruchomić fmt.Println goroutine.

+0

, więc pytanie "Dlaczego Locky nie jest blokowany przez ten wątek systemu operacyjnego?" Powinno brzmieć "dlaczego wiele goroutines działa, nawet jeśli GOMAXPROCS jest ustawione na 1" –

0

To wygląda mi na prawidłowe zachowanie. Z tego co rozumiem, funkcja LockOSThread() wiąże tylko wszystkie przyszłe wywołania do wątku pojedynczego systemu operacyjnego, nie przesyła ani nie zatrzymuje wątku w historii.

Edytuj dla jasności: jedyną rzeczą, która robi LockOSThread() jest wyłączenie wielowątkowego tak, aby wszystkie przyszłe wywołania GO odbywały się w jednym wątku. Jest to przede wszystkim do użytku z takimi rzeczami jak interfejsy API graficznego, które wymagają prawidłowego działania pojedynczego wątku.

Edytowany ponownie.

Jeśli porównać to:

func main() { 
    runtime.GOMAXPROCS(6) 
    //insert long running routine here. 
    go fmt.Println("This may run almost straight away if tho long routine uses a different thread") 
} 

do tego:

func main() { 
    runtime.GOMAXPROCS(6) 
    runtime.LockOSThread() 
    //insert long running routine here. 
    go fmt.Println("This will only run after the task above has completed") 
} 

Jeśli wstawić długą procedurę uruchomiony w miejscu wskazanym powyżej ówczesnego pierwszego bloku może działać niemal natychmiast, gdy długo rutyna działa na nowym wątku, ale w drugim przykładzie zawsze będzie musiała czekać na zakończenie procedury.

+0

Dodatkowe czytanie: http://stackoverflow.com/questions/25361831/benefits-of-runtime-lockosthread-in-golang – sorifiend

+3

Ale dokumentacja stwierdza, że ​​"* * nie ** inne goroutines mogą "wykonać w tym wątku. To myląca dokumentacja, jeśli cokolwiek. Nie mam zamiaru spać ani zatrzymywać wątku systemu operacyjnego, ale zapobiegam wykonaniu goroutine na linii 3 głównej. – EMBLEM

+0

Tak, to trochę zwodnicze. Jednakże, jeśli czytasz wkładkę w środku, mówi ona: "Dopóki wywoływana goroutina nie opuści - nie ma innej gorączki". Mówi się o tym goroutine, odnosząc się do czegoś innego, czego używasz po użyciu 'LockOSThread()'. Powinien prawdopodobnie mieć "może być uruchomiony" na końcu tego dokumentu. – sorifiend

1

Oto próbka systemu Windows, która prawdopodobnie pomoże Ci zrozumieć, co się dzieje. Wyświetla identyfikatory wątków, na których działają goryle. Musiał użyć funkcji syscall, więc działa tylko w systemie Windows. Ale możesz łatwo przenieść go do innych systemów.

package main 

import (
    "fmt" 
    "runtime" 
    "golang.org/x/sys/windows" 
) 

func main() { 
    runtime.GOMAXPROCS(1) 
    runtime.LockOSThread() 
    ch := make(chan bool, 0) 
    go func(){ 
     fmt.Println("2", windows.GetCurrentThreadId()) 
     <- ch 
    }() 
    fmt.Println("1", windows.GetCurrentThreadId()) 
    <- ch 
} 

Nie używam trybu uśpienia, aby zapobiec uruchomianiu kolejnego wątku przez śpiący gorynt. Kanał zablokuje i usunie gorutine z uruchomionej kolejki. Jeśli wykonasz kod, zobaczysz, że identyfikatory wątków są różne. Główna goroutina zablokowała jeden z wątków, więc środowisko wykonawcze musi odrodzić następną.

Jak już wiesz, GOMAXPROCS nie uniemożliwia środowisku runtime odradzania więcej wątków. GOMAXPROCS to więcej informacji o liczbie wątków, które mogą uruchamiać goroutines równolegle. Ale więcej wątków można utworzyć dla goroutinów, które czekają na zakończenie syscall, na przykład.

Po usunięciu runtime.LockOSThread() zobaczysz, że identyfikatory wątków są równe. To dlatego, że odczyt kanału blokuje goroutine i pozwala środowisku wykonawczemu na wykonanie egzekucji na inną goroutine bez tworzenia nowego wątku. W ten sposób wiele goroutines może wykonywać jednocześnie, nawet gdy GOMAXPROCS jest 1.

Powiązane problemy