This is a creation in Article, where the information may have evolved or changed.
Preface
In languages such as C/c++/java, we can directly obtain the thread ID, and then by mapping the thread ID and the two-level dispatch task ID relationship, you can print the current taskid in the log, that is, the user does not perceive the task ID of the printing, Adaptation Layer Unified package, This makes it easy to view or filter multi-threaded concurrent logs.
Goroutine is the implementation of lightweight threading in Golang, managed by Go runtime. Golang supports lightweight threading at the language level, called Ctrip. All system invoke operations provided by the Golang standard library (including, of course, all synchronous IO operations) will give the CPU to the other goroutine. This makes things so simple that switching management for lightweight threads does not depend on the threads and processes of the system, nor on the number of cores in the CPU.
Goroutine is very bright, but since the go1.4 version, the Goroutine ID cannot be obtained directly from Go runtime.
This is Golang developers deliberately, to avoid developers abusing Goroutine ID implementation Goroutine Local Storage (Java-like thread local Storage), because Goroutine Local Storage It is difficult to do garbage collection. So even though Go1.4 exposed the method before, it has now been hidden.
This decision is a bit unworthy, and it becomes difficult to view and filter high-concurrency logs. Although it is possible to use the ID of the business itself in the log, it is unacceptable for programmers pursuing clean code to add some of the parameters in many functions just for printing.
In this paper, I will find a simple and efficient and stable solution, and give the best practice.
There are several methods
Get through the Assembly
High complexity, the offset address may vary with the version, it is not recommended
Get through third-party libraries
Related third-party libraries can be found on GitHub, such as:
https://github.com/jtolds/glshttps://github.com/huandu/goroutine
Stability unknown, performance is not high, not recommended to use
Through runtime. Stack Fetch
It uses runtime. Stack information that writes the current stack information to a slice, and the first behavior of the stack is "Goroutine # # # # # [...], where the" # # "is the current Goroutine Id, and the Goid function can be implemented with this gimmick.
When using this method, the Goid function is implemented as follows:
func Goid() int { defer func() { if err := recover(); err != nil { fmt.Println("panic recover:panic info:%v", err) } }() var buf [64]byte n := runtime.Stack(buf[:], false) idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] id, err := strconv.Atoi(idField) if err != nil { panic(fmt.Sprintf("cannot get goroutine id: %v", err)) } return id}
By modifying the compiler source code gets
Adding function goid to the Go Source runtime package, calling the runtime's GETG function directly, has the advantage of being simple and efficient and stable, while each team can deploy its own microservices through the container.
This method is detailed in the "Best Practices" section.
Method Three and method four comparison
Using method three and method four respectively, the Goid function calls the performance data of 10,000 consecutive times as follows:
Method Three |
method Four |
> 50ms |
< 5us |
For method Three, obtaining stack information affects performance, so it is recommended to use the performance-insensitive scenario;
For method Four, it is most efficient to call the runtime's GETG function directly, so it is recommended to use a scenario with a demanding performance requirement.
This article focuses on performance, so use method four.
Best practices
Download the go1.4 version of the compiler
Download the go1.4 version of the compiler on the official website of Golang, with the following URL:
https://golang.org/dl/
Unzip, rename the Go folder into go1.4, and then move to the $home directory.
To modify the compiler code for the go1.7.3 version
Download the go1.7.3 version of the source code on the official website of Golang.
Edit the Src/runtime/proc.go file and add the function goid at the tail:
func Goid() int64 { _g_ := getg() return _g_.goid}
Run the Src/make.bash command (using the compiler in the $home/go1.4 directory by default) to compile the new version of the go1.7.3.
Once the compilation is complete, copy the Go folder to the Goroot directory to make it effective:
$ go versiongo version go1.7.3 linux/amd64
Test code
We simulate a completely parallel computing task: Calculate the sum of n integers. We can divide all the integers into m parts, and M is the number of CPUs. Let each CPU begin to compute the compute task assigned to it, and then add the calculation results of each CPU once again, so that you can get the sum of all n integers, the implementation code is as follows:
type Vector []intfunc (v Vector) DoSome(i, n int, u Vector, c chan int, add *int) int { for ; i < n; i++ { *add += u[i] } id := runtime.Goid(id) fmt.Println("id:", id) c <- 1 return 1}const NCPU = 16func (v Vector) DoAll(u Vector) int { c := make(chan int, NCPU) var add [NCPU]int sum := 0 for i := 0; i < NCPU; i++ { go v.DoSome(i * len(v) / NCPU, (i + 1)* len(v) / NCPU, u, c, &add[i]) } for i := 0; i < NCPU; i++ { <- c } for i := 0; i < NCPU; i++ { sum += add[i] } return sum}func main() { x := 0 y := 0 v := make(Vector, 160) for i := 0; i < 160; i++ { v[i] = i x += i } y = v.DoAll(v) fmt.Println("x =", x, "and y =", y)}
Log
By looking at the log, we have successfully obtained the Goroutine Id. A word, perfect!
id: 20id: 13id: 7id: 12id: 14id: 9id: 5id: 17id: 16id: 10id: 6id: 15id: 18id: 19id: 8id: 11x = 12720 and y = 12720
Adaptation Layer Encapsulation
We can easily encapsulate the log interfaces of third-party libraries such as Glog, and hide the Goid acquisition and printing process, making the user easy.
Summary
Aiming at the problem of difficult to view or filter the high concurrency log of goroutine in Golang, this paper analyzes the existing methods of acquiring Goroutine ID, and finally finds a simple and efficient method, that is, by modifying the source code of the compiler, and giving the best practice. Hope to have a certain help to the reader.