5
search [160b306cd6e77f brain into fried fish ] Follow this fried fish with liver fried. This article GitHub github.com/eddycjy/blog has been included, with my series of articles, materials and open source Go books.

Hello everyone, I am fried fish.

Recently, it is the season of interviews. In my Go reader exchange group, many friends appeared in some Go interview questions that they encountered during the interview process.

Today's protagonist is a point that everyone will easily get tangled when learning Goroutine when they have an existing language foundation. It means " processes and threads have IDs, why does Goroutine not have GoroutineID? ".

Why is this, and how to do those cross-coroutine processing?

What is GoroutineID

We need to know why everyone subconsciously wants to ask for GoroutineID, the following quotes the expression in the Go language bible:

In most operating systems and programming languages that support multithreading, the current thread has a unique identity (ID), and this identity information can be easily obtained in the form of a common value, typically a integer or pointer value. In this case, it is easy for us to make an abstract thread-local storage (thread local storage, content that other threads do not want to access in multi-threaded programming), and only need a map with the thread ID as the key to solve the problem , Each thread can get the value from it with its ID, and it does not conflict with other threads.

That is to say, there is the concept of ID in regular processes and threads. We can use ID in the program to obtain data in other processes and threads, and even transmit data. Like a key, you can do anything with him.

The concept of GoroutineID is similar, that is, the ID of the coroutine. We subconsciously expect to perform cross-coroutine operations through the coroutine ID.

However, there is no explicit way to obtain GoroutineID in the Go language, which is a big question.

Why is there no GoroutineID

Why is there no GoroutineID in the Go language? Is it absent from the beginning? Or, what is the reason for this design?

In fact, the Go language used to have an exposed method to obtain the GoroutineID, but this method was hidden after Go1.4, and it is not recommended for everyone to use it.

That is, there is no GoroutineID on the surface, which is an intentional behavior. The reason is: According to past experience, it is believed that thread-local storage has the possibility of being abused and brings a lot of unnecessary complexity .

Simply put, Andrew Gerrand's answer is " thread-local storage far exceeds their benefits. They are just not suitable for Go language ."

Potential problems

  • When Goroutine disappears:

    • Its Goroutine local storage will not be GC. (You can get the current Goroutine of the goid, but you cannot get the list of all running Goroutines)
  • What if the handler generates a new Goroutine by itself?

    • The new Goroutine loses its local storage of the existing Goroutine. Although you can guarantee that your code will not generate other Goroutines.
    • Generally speaking, you cannot be sure that the standard library or any third-party code will not do this.
  • The complexity and mental burden of Go applications have increased.

Abuse scenario

There is a Go application that provides HTTP services externally, which is Web Server. Go HTTP Server adopts the method of starting a new coroutine every time a request is made.

Assuming that cross-coroutine manipulation can be performed through GoroutineID, then it is possible that my Goroutine will appear, which is not necessarily determined by "I". Maybe the other GoroutineB being processed has quietly changed the behavior of my GoroutineA.

This may lead to a disaster problem, that is, when something goes wrong, you don't know who touched your cheese. Finding out the problem is simply a disaster.

If the module you maintain is clear, you at least know about this. Suppose your former colleague just left, and you are familiar with the code again, and there is a problem. This pot is firmly on your head.

How to get GoroutineID

We just mentioned that the GoroutineID was hidden on the bright side, but on the dark side, is there any other way to get it?

The answer is: yes.

It can be obtained by hacking code. of the Go language standard library http/2, the following methods are provided:

func main() {
    go func() {
        fmt.Println("脑子进煎鱼了的 GoroutineID:", curGoroutineID())
    }()

    time.Sleep(time.Second)
}

func curGoroutineID() 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)
    if err != nil {
        panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
    }
    return n
}

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

var goroutineSpace = []byte("goroutine ")

The output is:

脑子进煎鱼了的 GoroutineID: 18

Binding curGoroutineID method point of view, by analyzing the running time of the Go, i.e. runtime.Stack whereby GoroutineID.

Its role is mostly for tracking and debugging. Because the official does not provide a series of cross-coroutine manipulation methods based on GoroutineID.

There are also the following open source libraries that can be used to obtain GoroutineID (but they have not been maintained for many years):

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

davecheney/junk

That is, "If you use this package, you will go directly to hell." It is very fierce, and I deeply persuade everyone to use it.

Where is it common everyday

If you often work as a fire-fighting team leader, troubleshoot the problems in the Go project, such as: error stack information, PProf performance analysis and other debugging information.

Therefore, we often see GoroutineID, which is " goroutine #### [...]".

#### we see is the real GoroutineID, and the remaining information is some stack traces and error descriptions.

Should GoroutineID be used?

From the results, it is definitely not recommended to use GoroutineID. After all, there is no special benefit, and the Go team is also against it.

Therefore, the general answer is "Cannot obtain GoroutineID", and the language design concept should be followed Share Memory By Communicating to achieve cross-coroutine operation.

to sum up

In today's article, we sorted out GoroutineID's history, effects, reasons, and hacking methods one by one, and explored what is inside.

The comparison of processes, threads, and coroutines is a topic often asked in interviews, and GoroutineID is one of them, which involves the overall design considerations.

Have you ever encountered the use of GoroutineID and questions? Welcome everyone to leave a message to discuss.

If you have any questions please comment and feedback exchange area, best relationship is mutual achievement , everybody thumbs is fried fish maximum power of creation, thanks for the support.

The article is continuously updated, and you can search on [My brain is fried fish] to read, reply [160b306cd6ef22 000 ] I have prepared the first-line interview algorithm questions and information; this article GitHub github.com/eddycjy/blog has been included , Welcome Star to urge you to update.

煎鱼
8.4k 声望12.8k 粉丝