Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does it not create many threads when many goroutines are blocked in writing file in golang?

As we know in go, a thread may be created when the goroutine has to perform a blocking call, such as a system call, or a call to a C library via cgo. Some test code:

   package main

   import (
        "io/ioutil"
        "os"
        "runtime"
        "strconv"
    )

    func main() {
        runtime.GOMAXPROCS(2)
        data, err := ioutil.ReadFile("./55555.log")
        if err != nil {
            println(err)
            return
        }
        for i := 0; i < 200; i++ {
            go func(n int) {
                for {
                    err := ioutil.WriteFile("testxxx"+strconv.Itoa(n), []byte(data), os.ModePerm)
                    if err != nil {
                        println(err)
                        break
                    }
                }
            }(i)
        }
        select {}
    }

When I run it, it didn't create many threads.

➜ =99=[root /root]$ cat /proc/9616/status | grep -i thread
Threads:    5

Any ideas?

like image 512
frank.lin Avatar asked Jan 28 '15 06:01

frank.lin


People also ask

Do Goroutines use multiple threads?

The scheduler only uses one thread to run all goroutines, which means it only implements concurrency. If you want to use more CPU cores in order to take advantage of parallel processing, you have to call runtime. GOMAXPROCS(n) to set the number of cores you want to use.

What happens when Goroutine blocks?

When we send data into the channel using a GoRoutine, it will be blocked until the data is consumed by another GoRoutine. When we receive data from channel using a GoRoutine, it will be blocked until the data is available in the channel.

Why are Goroutines better than threads?

Goroutines have easy communication medium known as channel. Thread does not have easy communication medium. Due to the presence of channel one goroutine can communicate with other goroutine with low latency. Due to lack of easy communication medium inter-threads communicate takes place with high latency.

Are Goroutines single threaded?

At any time, at most only one thread is allowed to run per core. But scheduler can create more threads if required, but that rarely happens. If your program doesn't start any additional goroutines, it will naturally run in only one thread no matter how many cores you allow it to use.


2 Answers

I altered your program slightly to output a much bigger block

package main

import (
    "io/ioutil"
    "os"
    "runtime"
    "strconv"
)

func main() {
    runtime.GOMAXPROCS(2)
    data := make([]byte, 128*1024*1024)
    for i := 0; i < 200; i++ {
        go func(n int) {
            for {
                err := ioutil.WriteFile("testxxx"+strconv.Itoa(n), []byte(data), os.ModePerm)
                if err != nil {
                    println(err)
                    break
                }
            }
        }(i)
    }
    select {}
}

This then shows >200 threads as you expected

$ cat /proc/17033/status | grep -i thread
Threads:    203

So I think the syscalls were exiting too quickly in your original test to show the effect you were expecting.

like image 183
Nick Craig-Wood Avatar answered Sep 20 '22 17:09

Nick Craig-Wood


The issue 4056 discusses how to limit the number of actual threads (not goroutine) created.

Go 1.2 introduced that thread limit management in commit 665feee.

You can see a test to check if the number of thread created is actually reached or not in pkg/runtime/crash_test.go#L128-L134:

func TestThreadExhaustion(t *testing.T) {
    output := executeTest(t, threadExhaustionSource, nil)
    want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
    if !strings.HasPrefix(output, want) {
        t.Fatalf("output does not start with %q:\n%s", want, output)
    }
}

That same file has an example to create an actual thread (for a given goroutine), using runtime.LockOSThread():

func testInNewThread(name string) {
    c := make(chan bool)
    go func() {
        runtime.LockOSThread()
        test(name)
        c <- true
    }()
    <-c
}
like image 28
VonC Avatar answered Sep 24 '22 17:09

VonC