検索
ホームページバックエンド開発GolangGoroutine ID を取得するにはどうすればよいですか?

How to Get the Goroutine ID?

オペレーティング システムでは、各プロセスには一意のプロセス ID があり、各スレッドには独自の一意のスレッド ID があります。同様に、Go 言語では、各ゴルーチンには独自の一意の Go ルーチン ID があり、パニックなどのシナリオでよく発生します。 Goroutine には固有の ID がありますが、Go 言語はこの ID を取得するためのインターフェイスを意図的に提供していません。今回は、Go アセンブリ言語を使用して Goroutine ID を取得してみます。

1. goid を持たない公式デザイン(https://github.com/golang/go/issues/22770)

公式の関連資料によると、Go 言語が意図的に goid を提供しない理由は、悪用を避けるためです。なぜなら、ほとんどのユーザーは簡単に goid を取得すると、その後のプログラミングで無意識のうちに goid に強く依存するコードを書いてしまうからです。 goid への依存度が高いと、このコードの移植が困難になり、並行モデルも複雑になります。同時に、Go 言語には膨大な数のゴルーチンが存在する可能性がありますが、各ゴルーチンがいつ破棄されるかをリアルタイムで監視するのは簡単ではありません。そのため、goid に依存するリソースも自動的にリサイクルされなくなります (手動でのリサイクルが必要です)。ただし、Go アセンブリ言語のユーザーであれば、これらの懸念は完全に無視できます。

注意: goid を無理に取得すると「恥をかかされる」可能性があります ?:
https://github.com/golang/go/blob/master/src/runtime/proc.go#L7120

2. Pure Go で goid を取得する

理解を容易にするために、最初に純粋な Go で goid を取得してみます。純粋な Go で goid を取得するパフォーマンスは比較的低いですが、コードの移植性が高く、他のメソッドで取得した goid が正しいかどうかのテストや検証にも使用できます。

すべての Go 言語ユーザーはパニック関数を知っている必要があります。パニック関数を呼び出すと、Goroutine 例外が発生します。 Goroutine のルート関数に到達する前にリカバリ関数によってパニックが処理されなかった場合、ランタイムは関連する例外とスタック情報を出力し、Goroutine を終了します。

パニックを通じて goid を出力する簡単な例を構築してみましょう:

package main

func main() {
    panic("leapcell")
}

実行後、次の情報が出力されます:

panic: leapcell

goroutine 1 [running]:
main.main()
    /path/to/main.go:4 +0x40

Panic 出力情報 goroutine 1 [running] の 1 が goid であると推測できます。しかし、プログラム内でパニック出力情報を取得するにはどうすればよいでしょうか?実際、上記の情報は、現在の関数呼び出しスタック フレームのテキストによる説明にすぎません。 runtime.Stack関数はこれらの情報を取得する機能を提供します。

runtime.Stack 関数に基づいて例を再構築し、現在のスタック フレームの情報を出力することで goid を出力しましょう。

package main

func main() {
    panic("leapcell")
}

実行後、次の情報が出力されます:

panic: leapcell

goroutine 1 [running]:
main.main()
    /path/to/main.go:4 +0x40

そのため、runtime.Stack:
で取得した文字列から goid 情報を解析するのは簡単です。

package main

import "runtime"

func main() {
    var buf = make([]byte, 64)
    var stk = buf[:runtime.Stack(buf, false)]
    print(string(stk))
}

GetGoid 関数の詳細については説明しません。 runtime.Stack 関数は、現在の Goroutine のスタック情報だけでなく、すべての Goroutine のスタック情報 (2 番目のパラメーターで制御) も取得できることに注意してください。同時に、Go 言語の net/http2.curGoroutineID 関数も同様の方法で goid を取得します。

3. g 構造体から goid を取得する

Go アセンブリ言語の公式ドキュメントによると、実行中の各 Goroutine 構造体の g ポインタは、現在実行中の Goroutine が配置されているシステム スレッドのローカル ストレージ TLS に保存されます。まず TLS スレッドのローカル ストレージを取得し、次に TLS から g 構造体のポインタを取得し、最後に g 構造体から goid を抽出します。

次は、ランタイム パッケージで定義されている get_tls マクロを参照して g ポインターを取得します。

goroutine 1 [running]:
main.main()
    /path/to/main.g

get_tls は、runtime/go_tls.h ヘッダー ファイルで定義されたマクロ関数です。

AMD64 プラットフォームの場合、get_tls マクロ関数は次のように定義されます。

import (
    "fmt"
    "strconv"
    "strings"
    "runtime"
)

func GetGoid() int64 {
    var (
        buf [64]byte
        n   = runtime.Stack(buf[:], false)
        stk = strings.TrimPrefix(string(buf[:n]), "goroutine")
    )

    idField := strings.Fields(stk)[0]
    id, err := strconv.Atoi(idField)
    if err!= nil {
        panic(fmt.Errorf("can not get goroutine id: %v", err))
    }

    return int64(id)
}

get_tls マクロ関数を展開した後、g ポインターを取得するコードは次のとおりです。

get_tls(CX)
MOVQ g(CX), AX     // Move g into AX.

実際、TLS はスレッドのローカルストレージのアドレスに似ており、そのアドレスに対応するメモリ上のデータが g ポインタです。もっと単純に言うこともできます:

#ifdef GOARCH_amd64
#define        get_tls(r)        MOVQ TLS, r
#define        g(r)        0(r)(TLS*1)
#endif

上記のメソッドに基づいて、getg 関数をラップして g ポインターを取得できます。

MOVQ TLS, CX
MOVQ 0(CX)(TLS*1), AX

次に、Go コードで、g 構造体の goid メンバーのオフセットを通じて goid の値を取得します。

MOVQ (TLS), AX

ここで、g_goid_offset は goid メンバーのオフセットです。 g 構造体は runtime/runtime2.go.

を参照します。

Go1.10バージョンでは、goidのオフセットは152バイトです。したがって、上記のコードは、goid オフセットも 152 バイトである Go バージョンでのみ正しく実行できます。偉大なトンプソンの神託によると、列挙と力技はすべての困難な問題に対する万能薬です。 goid オフセットをテーブルに保存し、Go バージョン番号に基づいて goid オフセットをクエリすることもできます。

以下は改良されたコードです:

// func getg() unsafe.Pointer
TEXT ·getg(SB), NOSPLIT, <pre class="brush:php;toolbar:false">const g_goid_offset = 152 // Go1.10

func GetGroutineId() int64 {
    g := getg()
    p := (*int64)(unsafe.Pointer(uintptr(g) + g_goid_offset))
    return *p
}
-8 MOVQ (TLS), AX MOVQ AX, ret+0(FP) RET

これで、ついに goid オフセットがリリースされた Go 言語バージョンに自動的に適応できるようになりました。

4. g構造体に対応するインターフェースオブジェクトの取得

列挙と総当たりは簡単ですが、開発中の未リリースの Go バージョンを十分にサポートしていません。開発中の特定のバージョンにおける goid メンバーのオフセットを事前に知ることはできません。

ランタイム パッケージ内にある場合は、unsafe.OffsetOf(g.goid) を通じてメンバーのオフセットを直接取得できます。リフレクションを通じて g 構造体の型を取得し、その型を通じて特定のメンバーのオフセットをクエリすることもできます。 g 構造体は内部型であるため、Go コードは外部パッケージから g 構造体の型情報を取得できません。しかし、Go アセンブリ言語ではすべてのシンボルが見えるので、理論的には g 構造体の型情報も取得できます。

型が定義されると、Go 言語はその型に対応する型情報を生成します。たとえば、g 構造体は、g 構造体の値型情報を表す type-runtime-g 識別子と、ポインタ型情報を表す type-*runtime-g 識別子を生成します。 g 構造体にメソッドがある場合、メソッドで型情報を表す go.itab.runtime.g および go.itab.*runtime.g の型情報も生成されます。

g 構造体の型を表す type・runtime・g と g ポインタを取得できれば、g オブジェクトのインターフェースを構築できます。以下は、g ポインター オブジェクトのインターフェイスを返す、改良された getg 関数です。

package main

func main() {
    panic("leapcell")
}

ここで、AXレジスタはgポインタに相当し、BXレジスタはg構造体の型に相当します。次に、runtime・convT2E 関数を使用して型をインターフェイスに変換します。 g 構造体のポインター型を使用していないため、返されるインターフェイスは g 構造体の値の型を表します。理論的には、 g ポインター型のインターフェイスを構築することもできますが、Go アセンブリ言語の制限のため、type·*runtime·g 識別子は使用できません。

g によって返されたインターフェースに基づいて、goid を簡単に取得できます。

panic: leapcell

goroutine 1 [running]:
main.main()
    /path/to/main.go:4 +0x40

上記のコードは、リフレクションを通じて goid を直接取得します。理論的には、反映されたインターフェイスと goid メンバーの名前が変更されない限り、コードは正常に実行できます。実際のテストの後、上記のコードは Go1.8、Go1.9、および Go1.10 バージョンで正しく実行できます。楽観的に考えれば、g 構造型の名前が変更されず、Go 言語のリフレクション メカニズムが変更されなければ、将来の Go 言語バージョンでも実行できるはずです。

リフレクションにはある程度の柔軟性がありますが、リフレクションのパフォーマンスは常に批判されてきました。改善されたアイデアは、リフレクションを通じて goid のオフセットを取得し、次に g ポインターとオフセットを通じて goid を取得することです。これにより、リフレクションは初期化フェーズで 1 回だけ実行する必要があります。

以下は g_goid_offset 変数の初期化コードです:

package main

import "runtime"

func main() {
    var buf = make([]byte, 64)
    var stk = buf[:runtime.Stack(buf, false)]
    print(string(stk))
}

正しい goid オフセットを取得した後、前述の方法で goid を取得します。

package main

func main() {
    panic("leapcell")
}

この時点で、goid を取得するための実装アイデアは十分に完成しましたが、アセンブリ コードには依然として重大なセキュリティ リスクが存在します。

getg 関数は NOSPLIT フラグでスタック分割を禁止する関数型として宣言されていますが、getg 関数は内部的にはより複雑な runtime・convT2E 関数を呼び出します。 runtime·convT2E 関数でスタック領域が不足している場合、スタック分割操作がトリガーされる可能性があります。スタックが分割されると、GC は関数パラメーター、戻り値、およびローカル変数内のスタック ポインターを移動します。ただし、getg 関数はローカル変数のポインター情報を提供しません。

以下は、改良された getg 関数の完全な実装です:

panic: leapcell

goroutine 1 [running]:
main.main()
    /path/to/main.go:4 +0x40

ここで、NO_LOCAL_POINTERS は、関数にローカル ポインター変数がないことを意味します。同時に、返されたインターフェイスはゼロ値で初期化され、初期化が完了した後、GO_RESULTS_INITIALIZED を使用して GC に通知されます。これにより、スタックが分割されたときに、GC が戻り値とローカル変数のポインターを正しく処理できることが保証されます。

5. goidの適用:ローカルストレージ

goid を使用すると、Goroutine のローカル ストレージを非常に簡単に構築できます。 goid 機能を提供する gls パッケージを定義できます。

package main

import "runtime"

func main() {
    var buf = make([]byte, 64)
    var stk = buf[:runtime.Stack(buf, false)]
    print(string(stk))
}

gls パッケージ変数は単純にマップをラップし、sync.Mutex ミューテックスを介した同時アクセスをサポートします。

次に、内部 getMap 関数を定義して、Goroutine バイトごとにマップを取得します。

goroutine 1 [running]:
main.main()
    /path/to/main.g

Goroutine のプライベート マップを取得した後は、追加、削除、および変更操作のための通常のインターフェイスになります。

import (
    "fmt"
    "strconv"
    "strings"
    "runtime"
)

func GetGoid() int64 {
    var (
        buf [64]byte
        n   = runtime.Stack(buf[:], false)
        stk = strings.TrimPrefix(string(buf[:n]), "goroutine")
    )

    idField := strings.Fields(stk)[0]
    id, err := strconv.Atoi(idField)
    if err!= nil {
        panic(fmt.Errorf("can not get goroutine id: %v", err))
    }

    return int64(id)
}

最後に、Goroutine に対応するマップ リソースを解放する Clean 関数を提供します。

get_tls(CX)
MOVQ g(CX), AX     // Move g into AX.

このようにして、最小限の Goroutine ローカル ストレージ gls オブジェクトが完成します。

次に、ローカル ストレージを使用する簡単な例を示します。

#ifdef GOARCH_amd64
#define        get_tls(r)        MOVQ TLS, r
#define        g(r)        0(r)(TLS*1)
#endif

Goroutine ローカル ストレージを介して、さまざまなレベルの関数がストレージ リソースを共有できます。同時に、リソース リークを避けるために、Goroutine のルート関数で gls.Clean() 関数を defer ステートメント経由で呼び出してリソースを解放する必要があります。

Leapcell: Golang アプリケーションをホストするための高度なサーバーレス プラットフォーム

How to Get the Goroutine ID?

最後に、Go サービスのデプロイに最も適したプラットフォームをお勧めします。leapcell

1. 多言語サポート

  • JavaScript、Python、Go、または Rust を使用して開発します。

2. 無制限のプロジェクトを無料でデプロイ

  • 使用料金のみお支払いください。リクエストや料金はかかりません。

3. 比類のないコスト効率

  • アイドル料金なしの従量課金制。
  • 例: $25 は、平均応答時間 60 ミリ秒で 694 万のリクエストをサポートします。

4. 合理化された開発者エクスペリエンス

  • 直感的な UI でセットアップが簡単。
  • 完全に自動化された CI/CD パイプラインと GitOps の統合。
  • 実用的な洞察を得るリアルタイムのメトリクスとログ。

5. 容易な拡張性と高性能

  • 自動スケーリングにより、高い同時実行性を簡単に処理できます。
  • 運用上のオーバーヘッドがゼロ - 構築だけに集中できます。

ドキュメントでさらに詳しく見てみましょう!

Leapcell Twitter: https://x.com/LeapcellHQ

以上がGoroutine ID を取得するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
Golang vs. Python:長所と短所Golang vs. Python:長所と短所Apr 21, 2025 am 12:17 AM

GolangisidealforBuildingsCalables Systemsduetoitsefficiency andConcurrency、Whilepythonexcelsinquickscriptinganddataanalysisduetoitssimplicityand vastecosystem.golang'ssignencouragesclean、readisinediteNeditinesinedinediseNabletinedinedinedisedisedioncourase

Golang and C:Concurrency vs. Raw SpeedGolang and C:Concurrency vs. Raw SpeedApr 21, 2025 am 12:16 AM

Golangは並行性がCよりも優れていますが、Cは生の速度ではGolangよりも優れています。 1)Golangは、GoroutineとChannelを通じて効率的な並行性を達成します。これは、多数の同時タスクの処理に適しています。 2)Cコンパイラの最適化と標準ライブラリを介して、極端な最適化を必要とするアプリケーションに適したハードウェアに近い高性能を提供します。

なぜゴランを使うのですか?説明された利点と利点が説明されていますなぜゴランを使うのですか?説明された利点と利点が説明されていますApr 21, 2025 am 12:15 AM

Golangを選択する理由には、1)高い並行性パフォーマンス、2)静的タイプシステム、3)ガベージ収集メカニズム、4)豊富な標準ライブラリとエコシステムは、効率的で信頼できるソフトウェアを開発するための理想的な選択肢となります。

Golang vs. C:パフォーマンスと速度の比較Golang vs. C:パフォーマンスと速度の比較Apr 21, 2025 am 12:13 AM

Golangは迅速な発展と同時シナリオに適しており、Cは極端なパフォーマンスと低レベルの制御が必要なシナリオに適しています。 1)Golangは、ごみ収集と並行機関のメカニズムを通じてパフォーマンスを向上させ、高配列Webサービス開発に適しています。 2)Cは、手動のメモリ管理とコンパイラの最適化を通じて究極のパフォーマンスを実現し、埋め込みシステム開発に適しています。

GolangはCよりも速いですか?制限の調査GolangはCよりも速いですか?制限の調査Apr 20, 2025 am 12:19 AM

Golangは、コンピレーション時間と同時処理においてより良いパフォーマンスを発揮しますが、Cはランニング速度とメモリ管理においてより多くの利点があります。 1.Golangの編集速度は速く、迅速な発展に適しています。 2.Cは速く実行され、パフォーマンスクリティカルなアプリケーションに適しています。 3. Golangは、同時処理においてシンプルで効率的で、同時プログラミングに適しています。 4.Cマニュアルメモリ管理により、パフォーマンスが高くなりますが、開発の複雑さが向上します。

Golang:WebサービスからシステムプログラミングまでGolang:WebサービスからシステムプログラミングまでApr 20, 2025 am 12:18 AM

WebサービスとシステムプログラミングへのGolangのアプリケーションは、主にそのシンプルさ、効率性、並行性に反映されています。 1)Webサービスでは、Golangは、強力なHTTPライブラリと同時処理機能を介して、高性能WebアプリケーションとAPIの作成をサポートしています。 2)システムプログラミングでは、Golangはハードウェアに近い機能とC言語との互換性を使用して、オペレーティングシステムの開発と組み込みシステムに適しています。

Golang vs. C:ベンチマークと現実世界のパフォーマンスGolang vs. C:ベンチマークと現実世界のパフォーマンスApr 20, 2025 am 12:18 AM

GolangとCには、パフォーマンスの比較に独自の利点と欠点があります。1。ゴーランは、高い並行性と迅速な発展に適していますが、ごみ収集はパフォーマンスに影響を与える可能性があります。 2.Cは、パフォーマンスとハードウェア制御を高くしますが、開発の複雑さが高くなります。選択を行うときは、プロジェクトの要件とチームのスキルを包括的な方法で考慮する必要があります。

Golang vs. Python:比較分析Golang vs. Python:比較分析Apr 20, 2025 am 12:17 AM

Golangは、高性能および同時プログラミングシナリオに適していますが、Pythonは迅速な開発とデータ処理に適しています。 1.Golangは、シンプルさと効率性を強調し、バックエンドサービスとマイクロサービスに適しています。 2。Pythonは、データサイエンスと機械学習に適した簡潔な構文とリッチライブラリで知られています。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

SublimeText3 Linux 新バージョン

SublimeText3 Linux 新バージョン

SublimeText3 Linux 最新バージョン

AtomエディタMac版ダウンロード

AtomエディタMac版ダウンロード

最も人気のあるオープンソースエディター

SecLists

SecLists

SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)