Wechat search [brain into fried fish] follow this fried fish with fried liver. In this paper, making github.com/eddycjy/blo… Already included, there are my series of articles, materials and open source Go books.

Hello, I’m fried fish.

It’s the interview season these days. In my Go reader exchange group, there appeared a lot of friends discussing some Go interview questions they met in the interview process.

Today, I’m going to talk about something that you might struggle with when you learn Goroutine if you already know the language. Why does Goroutine not have a GoroutineID? .

Why is that? How do you do all that cross-coroutine processing?

What is GoroutineID

We need to know why people subconsciously want GoroutineID, to quote from the Bible of the Go language:

In most operating systems and programming languages that support multithreading, the current thread has a unique identity (ID) that can be easily obtained in the form of a common value, typically an INTEGER or pointer value. In this case, it is very easy to create an abstract thread-local storage. We just need a map with the thread ID as the key to solve the problem. Each thread gets a value from it with its ID, and does not conflict with other threads.

There is the concept of ID in the normal process and thread. We can obtain data in other processes and threads through ID in the program, and even transmit data. It’s like a key. You can do anything with him.

The GoroutineID concept is similar, that is, the ID of the coroutine. We subconsciously expect cross-coroutine operations via coroutine ids.

However, there is no explicit way to get GoroutineID in Go, which is a big puzzle.

Why is there no GoroutineID

Why is there no GoroutineID in Go, in the first place, or is there a reason for the design?

The Go language used to have an exposed method to get GoroutineID, but since Go1.4 this method has been hidden and is not recommended.

The apparent absence of GoroutineID is a deliberate act. The reason is as follows: Based on previous experience, thread-local storage is likely to be abused and may cause unnecessary complexity.

Simply put, Andrew Gerrand’s answer is that “the costs of thread-local storage far exceed their benefits. They just don’t fit the Go language.”

Potential problems

  • When Goroutine disappears:

    • Its Goroutine local storage will not be GC. (You can get the current Goroutine of a goID, but you can’t get a list of all running Goroutines)
  • What if the handler creates a new Goroutine of its own?

    • The new Goroutine loses local storage for the existing Goroutine. Although you can guarantee that your code will not produce other Goroutines.
    • In general, you can’t guarantee that the standard library or any third party code won’t do this.
  • Go applications increase in complexity, mental load, etc.

Scenarios of abuse

There is a Go application that provides HTTP services externally, that is, Web Server. Go HTTP Server uses a new coroutine for each request.

Given that cross-coroutine manipulation is possible through GoroutineID, it is possible to have my Goroutine, not necessarily determined by “I” myself. Perhaps the other GoroutineB that is being dealt with has quietly changed my GoroutineA’s behavior.

This can lead to a disaster problem, where you don’t know who moved your cheese when something went wrong. The investigation was a disaster.

If the modules you maintain are clear, you should at least know this. Let’s say your former colleague has just left the company, and you’re familiarizing yourself with the code. The pot is on your head like hell.

How do I get the GoroutineID

We mentioned that the GoroutineID is hidden on the surface, but what about the dark side? Is there another way to get it?

The answer is: yes.

We can get it by hacking code. Gotrack, the standard library of Go language HTTP /2, provides the following methods:

func main(a) {
    go func(a) {
        fmt.Println("Brain goes into fried fish GoroutineID:", curGoroutineID())
    }()

    time.Sleep(time.Second)
}

func curGoroutineID(a) uint64 {
    bp := littleBuf.Get().(*[]byte)
    defer littleBuf.Put(bp)
    b := *bp
    b = b[:runtime.Stack(b, false)]
    // Parse the 4707 out of "goroutine 4707 ["
    b = bytes.TrimPrefix(b, goroutineSpace)
    i := bytes.IndexByte(b, ' ')
    if i < 0 {
        panic(fmt.Sprintf("No space found in %q", b))
    }
    b = b[:i]
    n, err := parseUintBytes(b, 10.64)
    iferr ! =nil {
        panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
    }
    return n
}

var littleBuf = sync.Pool{
    New: func(a) interface{} {
        buf := make([]byte.64)
        return &buf
    },
}

var goroutineSpace = []byte("goroutine ")
Copy the code

The output is:

Brain goes into fried fish GoroutineID: 18Copy the code

Combined with the curGoroutineID method, GoroutineID can be obtained by analyzing the Go runtime, namely runtime.Stack.

Its function, more is to carry on the tracking and debugging function mostly. Because GoroutineID does not officially provide a set of methods for cross-coroutine manipulation.

There are also the following open source libraries that can be used to get GoroutineID (although neither has been maintained for years) :

  • davecheney/junk
  • jtolio/gls
  • tylerstillwater/gls

Dave Cheney of the Go team commented on his open source GoroutineID library: “If you use this package, you will Go straight to hell.”

If you use this bag, you will go straight to hell. “Very fierce, deeply discourage everyone to use.

Where is daily life common

If you are often the fire brigade leader, Go to Go project problems, such as: error stack information, PProf performance analysis and other debugging information.

So it’s common to see GoroutineID, which is “goroutine #### […] “.

What we see #### is the actual GoroutineID, and the rest of the information is a stack trace and error description.

Should I use GoroutineID?

Judging from the results, GoroutineID is definitely not recommended. After all, there was no particular benefit, and the Go team was against it.

Therefore, one will answer “GoroutineID cannot be obtained” directly. It will be more reasonable to follow the language design concept and use Share Memory By Communicating to achieve cross-coroutine manipulation.

conclusion

In this article, we have sorted out GoroutineID’s history, functions, reasons and hacking methods one by one to explore what it is.

The comparison between processes, threads, and coroutines is a common interview topic, and GoroutineID is one of them, which involves overall design considerations.

Have you ever encountered GoroutineID usage and query scenarios, welcome to leave a comment to discuss.

If you have any questions, welcome feedback and communication in the comments section. The best relationship is mutual achievement. Your praise is the biggest motivation for the creation of fried fish, thank you for your support.

This article is constantly updated. You can search “Brain into fried fish” on wechat to read it. Reply [000] There are the answers and materials for the interview algorithm of first-line big factories that I prepared. In this paper, making github.com/eddycjy/blo… Star support is welcome.