Goroutine は Go 言語の軽量スレッド実装であり、スレッド上に構築された軽量の抽象化であり、Go ランタイムによって管理されます。 Goroutine を使用すると、同じアドレス空間で複数の関数やメソッドを非常に低コストで並行して実行できます。スレッドと比較して、その作成と破棄のコストがはるかに低く、そのスケジューリングはスレッドから独立しています。
このチュートリアルの動作環境: Windows 7 システム、GO バージョン 1.18、Dell G3 コンピューター。
Socket ネットワークプログラムを作成する場合、パケットを送受信する Socket ごとにスレッドを割り当てるスレッド プールを事前に準備する必要があります。開発者は、スレッド間での複数のタスクの頻繁な切り替えによる効率の損失を回避しながら、各タスクが処理のために CPU にタイムリーに割り当てられるように、スレッド数と CPU 数の対応関係を確立する必要があります。 。
ただし、スレッド プールは、ロジック作成者にスレッド割り当てのための抽象的なメカニズムを提供します。ただし、いつでもどこでも発生する可能性のある同時実行性とスレッド処理の要件に直面した場合、スレッド プールはあまり直感的で便利ではありません。メカニズムはありますか: ユーザーが十分なタスクを割り当てた場合、システムはユーザーがタスクを CPU に割り当て、これらのタスクができるだけ同時に実行できるように自動的に支援します。この仕組みをGo言語ではゴルーチンと呼びます。
Goroutine は Go 言語の軽量スレッド実装であり、Go ランタイムによって管理されます。 Go プログラムは、ゴルーチン内のタスクを各 CPU にインテリジェントに割り当てます。
Goroutine は、スレッド上に構築された軽量の抽象化です。これにより、複数の関数やメソッドを同じアドレス空間で非常に低コストで並行して実行できるようになります。スレッドと比較して、その作成と破棄ははるかに低コストであり、そのスケジューリングはスレッドから独立しています。
Go プログラムはメイン パッケージの main() 関数から開始されます。プログラムが開始されると、Go プログラムは main() 関数のデフォルトの goroutine を作成します。
通常の関数を使用して goroutine を作成する
Go プログラムで go キーワードを使用して、関数の goroutine を作成します。関数は複数のゴルーチンを使用して作成できますが、1 つのゴルーチンは 1 つの関数に対応する必要があります。
1) 形式
共通関数のゴルーチンの作成は次のように記述します。
go 函数名( 参数列表 )
関数名: to関数名と呼ばれます。
パラメータ リスト: 関数を呼び出すときに渡す必要があるパラメータ。
go キーワードを使用して goroutine を作成する場合、呼び出された関数の戻り値は無視されます。
Goroutine でデータを返す必要がある場合は、後で紹介するチャネル機能を使用して、Goroutine からのデータを戻り値としてチャネル経由で渡してください。
2) 例
go キーワードを使用して、running() 関数を同時に実行し、メインの goroutine がユーザー入力を待機している間、毎秒カウンターを出力します。アクションを同時に実行できます。次のコードを参照してください:
package main import ( "fmt" "time" ) func running() { var times int // 构建一个无限循环 for { times++ fmt.Println("tick", times) // 延时1秒 time.Sleep(time.Second) } } func main() { // 并发执行程序 go running() // 接受命令行输入, 不做任何事情 var input string fmt.Scanln(&input) }
コマンド ラインの出力は次のとおりです:
コードが実行されると、コマンド ラインは継続的にティックを出力します。 fmt.Scanln () はユーザー入力を受け入れます。両方のステップを同時に実行できます。
コードの説明は次のとおりです。
12 行目で、for を使用して無限ループを形成します。
13 行目では、times 変数がループ内で増加し続けます。
図: 同時実行図
匿名関数を使用した goroutine の作成
go キーワードの後に匿名関数またはクロージャの goroutine を開始することもできます。1) 匿名関数を使用してゴルーチンを作成する形式
匿名関数またはクロージャを使用してゴルーチンを作成する場合、go の後に関数定義部分を記述するだけでなく、また、匿名関数の呼び出しパラメータを次の形式で追加する必要があります:go func( 参数列表 ){ 函数体 }( 调用参数列表 )
调用参数列表:启动 goroutine 时,需要向匿名函数传递的调用参数。
2) 使用匿名函数创建goroutine的例子
在 main() 函数中创建一个匿名函数并为匿名函数启动 goroutine。匿名函数没有参数。代码将并行执行定时打印计数的效果。参见下面的代码:
package main import ( "fmt" "time" ) func main() { go func() { var times int for { times++ fmt.Println("tick", times) time.Sleep(time.Second) } }() var input string fmt.Scanln(&input) }
代码说明如下:
第 10 行,go 后面接匿名函数启动 goroutine。
第 12~19 行的逻辑与前面程序的 running() 函数一致。
第 21 行的括号的功能是调用匿名函数的参数列表。由于第 10 行的匿名函数没有参数,因此第 21 行的参数列表也是空的。
扩展知识:Goroutine与线程的区别
许多人认为goroutine比线程运行得更快,这是一个误解。Goroutine并不会更快,它只是增加了更多的并发性。当一个goroutine被阻塞(比如等待IO),golang的scheduler会调度其他可以执行的goroutine运行。与线程相比,它有以下的几个优点:
内存消耗更少:
Goroutine所需要的内存通常只有2kb,而线程则需要1Mb(500倍)
创建与销毁的开销更小:
由于线程创建时需要向操作系统申请资源,并且在销毁时将资源归还,因此它的创建和销毁的开销比较大。相比之下,goroutine的创建和销毁是由go语言在运行时自己管理的,因此开销更低。
切换开销更小:
只是goroutine之于线程的主要区别,也是golang能够实现高并发的主要原因。线程的调度方式是抢占式的,如果一个线程的执行时间超过了分配给它的时间片,就会被其他可执行的线程抢占。在线程切换的过程中需要保存/恢复所有的寄存器信息,比如16个通用寄存器,PC(Program Counter)、SP(Stack Pointer)段寄存器等等。而goroutine的调度是协同式的,它不会直接地与操作系统内核打交道。当goroutine进行切换的时候,之后很少量的寄存器需要保存和恢复(PC和SP)。因此goroutine的切换效率更高。
以上がGo言語のゴルーチンとは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。