Best practices for getting Goroutine IDs

Source: Internet
Author: User
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.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.