Why doesn't LockOSThread block this OS thread?

The documentation for runtime.LockOsThread states:

LockOSThread connects the calling call to the current thread of the operating system. Until the caller goroutine exits or calls UnlockOSThread, it will always be executed in this thread, and no other goroutine can.

But consider this 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) } 

Gorotin main connected to one available OS thread installed by GOMAXPROCS , so I expect that the goroutine created on line 3 of main will not start. But instead, the program prints This shouldn't run , pauses for 1 second and exits. Why is this happening?

+7
multithreading go
source share
3 answers

In the runtime documentation:

The GOMAXPROCS variable limits the number of operating system threads that can simultaneously execute GO code at the user level. There is no limit on the number of threads that can be blocked in system calls on behalf of Go code; they do not take into account the GOMAXPROCS limit.

A sleeping thread does not take into account the value of GOMAXPROCS 1, so Go may have a different thread that runs gotoutine fmt.Println .

+6
source share

Here is an example of Windows that will probably help you understand what is going on. It prints the identifiers of the threads running goroutines. I had to use syscall, so it only works on Windows. But you can easily transfer it to other systems.

 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 } 

I do not use sleep to prevent runtime from spawning another thread to mate goroutine. The channel will block and simply remove goroutine from the execution queue. If you execute the code, you will see that the thread IDs are different. The main goroutine blocked one of the threads, so the runtime should spawn another one.

As you already know, GOMAXPROCS does not prevent runtime from creating new threads. GOMAXPROCS is more about the number of threads that goroutines can execute in parallel. But more threads can be created for goroutines, which wait for syscall to complete, for example.

If you delete runtime.LockOSThread() , you will see that the thread IDs are equal. This is because reading a channel blocks goroutine and allows execution to execute execution on another goroutine without creating a new thread. That several gorut can be executed at the same time, even when GOMAXPROCS is 1.

+1
source share

This seems like the right behavior for me. From what I understand, the LockOSThread() function only binds all future calls to a single OS thread, it does not sleep and does not stop the thread ever.

Edit for clarity: the only thing LockOSThread() does is disable multithreading so that all future GO calls occur on the same thread. This is primarily for use with things like the graphical API, which requires a single thread to work correctly.

Edited again.

If you compare this:

 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") } 

For this:

 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") } 

If we insert a long subroutine at the location indicated above, then the first block can work almost immediately if the long procedure starts in a new thread, but in the second example it will always have to wait for the procedure to complete.

0
source share

All Articles