目次
Go の同時実行性 (マルチスレッド)
ホームページ バックエンド開発 Golang シングルスレッドは Go 言語の機能ですか?

シングルスレッドは Go 言語の機能ですか?

Jan 06, 2023 am 11:17 AM
golang 言語を移動

シングルスレッドは Go 言語の機能ではなく、Go 言語はマルチスレッドです。 Golang のスレッド モデルは MPG モデルです。全体的に、Go プロセスとカーネル スレッドは多対多の対応関係にあるため、Go はマルチスレッド モードである必要があります。M スレッドとカーネル スレッドは 1 対 1 に対応し、複数の G は複数の M に対応します。同様に、P はコンテキスト リソースを指します。

シングルスレッドは Go 言語の機能ですか?

このチュートリアルの動作環境: Windows 7 システム、GO バージョン 1.18、Dell G3 コンピューター。

シングルスレッドは Go 言語の機能ではなく、Go 言語はマルチスレッドです。 シングルスレッドだとまだ恥ずかしい、マルチコア時代の高い同時実行性を求めて生まれたと言われている言語なのでしょうか?

Golang のスレッド モデルは MPG モデルですが、一般的に Go プロセスとカーネル スレッドは多対多に対応するため、まずマルチスレッド化する必要があります。このうち、M はカーネル スレッドに 1:1 で対応し、複数の G は複数の M に対応します。P はコンテキスト リソースを指しますが、あまり言うことはありません。 M (またはカーネル スレッド) の数はいつ増加しますか?つまり、現在の M の数では現在の G をすべて移動するようにスケジュールできない場合、新しい M がそれを処理するために使用されます。

Go の同時実行性 (マルチスレッド)

Go を 21 世紀の C 言語と比較する人がいますが、それはまず、Go 言語の設計がシンプルだからです。次に、21 世紀で最も人気のある言語ですが、重要なのは並列プログラミングであり、Go は言語レベルから並列処理をサポートしています。

ゴルーチン

ゴルーチンは、Go の並列設計の中核です。最終的な分析では、ゴルーチンは実際にはスレッドですが、スレッドよりも小さいです。12 個のゴルーチンが下部の 5 つまたは 6 つのスレッドに反映される場合があります。Go 言語は、これらのゴルーチン間のメモリ共有を実現するのに役立ちます。 goroutine の実行に必要なスタック メモリは非常に少なく (約 4 ~ 5KB)、もちろん対応するデータに応じてスケールされます。このため、何千もの同時タスクを同時に実行できます。 Goroutine はスレッドよりも使いやすく、効率的で、軽量です。

Goroutine は Go のランタイムによって管理されるスレッド マネージャーです。 Goroutine は go キーワードによって実装されますが、これは実際には通常の関数です。

go hello(a, b, c)
ログイン後にコピー

キーワード go を使用して goroutine を開始します。例を見てみましょう

package main

import (
    "fmt"
    "runtime"
)

func say(s string) {
    for i := 0; i < 5; i++ {
        runtime.Gosched()
        fmt.Println(s)
    }
}

func main() {
    go say("world") //开一个新的Goroutines执行
    say("hello") //当前Goroutines执行
}

// 以上程序执行后将输出:
// hello
// world
// hello
// world
// hello
// world
// hello
// world
// hello
ログイン後にコピー

go キーワードを使用すると並行プログラミングを簡単に実装できることがわかります。上記の複数のゴルーチンは同じプロセス内で動作し、メモリデータを共有しますが、共有によって通信するのではなく、通信によって共有するという設計に従う必要があります。

runtime.Gosched() は、CPU にタイム スライスを他の時間に譲り、次回のある時点で goroutine の実行を再開し続けることを意味します。

デフォルトでは、スケジューラは単一のスレッドのみを使用します。つまり、同時実行のみが実現されます。マルチコア プロセッサの並列処理を利用するには、プログラム内で runtime.GOMAXPROCS(n) を明示的に呼び出して、複数のスレッドを同時に使用するようにスケジューラに指示する必要があります。 GOMAXPROCS は、ロジック コードを同時に実行できるシステム スレッドの最大数を設定し、前の設定を返します。 n < 1 の場合、現在の設定は変更されません。これは、Go の将来のバージョンでスケジュール設定が改善されると削除される予定です。

#channels

ゴルーチンは同じアドレス空間で実行されるため、共有メモリへのアクセスは同期する必要があります。では、ゴルーチン間でデータを通信するにはどうすればよいでしょうか? Go は優れた通信メカニズム チャネルを提供します。チャネルは、Unix シェルの双方向パイプにたとえることができます。チャネルを通じて値を送受信できます。これらの値は、特定のタイプ (チャネル タイプ) のみにすることができます。チャネルを定義するときは、チャネルに送信される値のタイプも定義する必要があります。チャネルを作成するには make を使用する必要があることに注意してください:

ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
ログイン後にコピー

チャネルは演算子を通じてデータを送受信します

<-

ch <- v    // 发送v到channel ch.
v := <-ch  // 从ch中接收数据,并赋值给v
ログイン後にコピー

これらを例に適用します。

package main

import "fmt"

func sum(a []int, c chan int) {
    total := 0
    for _, v := range a {
        total += v
    }
    c <- total  // send total to c
}

func main() {
    a := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)
    x, y := <-c, <-c  // receive from c

    fmt.Println(x, y, x + y)
}
ログイン後にコピー

デフォルトでは、相手側の準備が整わない限りチャネルのデータの送受信はブロックされるため、明示的なロックを必要とせずに Goroutine の同期が簡単になります。いわゆるブロッキングとは、(value := <-ch) を読み取ると、データが受信されるまでブロックされることを意味します。次に、データが読み取られるまで、送信 (ch<-5) はブロックされます。バッファなしチャネルは、複数のゴルーチン間を同期するための優れたツールです。

バッファされたチャネル

上記ではデフォルトの非キャッシュ タイプのチャネルを紹介しましたが、Go ではチャネルを指定することもできます。バッファ サイズは、単にチャネルが格納できる要素の数です。 ch:= make(chan bool, 4) は、4 つの要素を格納できる bool 型チャネルを作成します。このチャネルでは、最初の 4 つの要素をブロックせずに書き込むことができます。 5 番目の要素が書き込まれると、別の goroutine がチャネルからいくつかの要素を読み取ってスペースを空けるまで、コードはブロックされます。

ch := make(chan type, value)

value == 0 ! 无缓冲(阻塞)
value > 0 ! 缓冲(非阻塞,直到value 个元素)
ログイン後にコピー

我们看一下下面这个例子,你可以在自己本机测试一下,修改相应的value值

package main

import "fmt"

func main() {
    c := make(chan int, 2)//修改2为1就报错,修改2为3可以正常运行
    c <- 1
    c <- 2
    fmt.Println(<-c)
    fmt.Println(<-c)
}
    //修改为1报如下的错误:
    //fatal error: all goroutines are asleep - deadlock!
ログイン後にコピー

Range和Close

上面这个例子中,我们需要读取两次c,这样不是很方便,Go考虑到了这一点,所以也可以通过range,像操作slice或者map一样操作缓存类型的channel,请看下面的例子

package main

import (
    "fmt"
)

func fibonacci(n int, c chan int) {
    x, y := 1, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x + y
    }
    close(c)
}

func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    for i := range c {
        fmt.Println(i)
    }
}
ログイン後にコピー

for i := range c能够不断的读取channel里面的数据,直到该channel被显式的关闭。上面代码我们看到可以显式的关闭channel,生产者通过内置函数close关闭channel。关闭channel之后就无法再发送任何数据了,在消费方可以通过语法v, ok := <-ch测试channel是否被关闭。如果ok返回false,那么说明channel已经没有任何数据并且已经被关闭。

记住应该在生产者的地方关闭channel,而不是消费的地方去关闭它,这样容易引起panic

另外记住一点的就是channel不像文件之类的,不需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的结束range循环之类的

Select

我们上面介绍的都是只有一个channel的情况,那么如果存在多个channel的时候,我们该如何操作呢,Go里面提供了一个关键字select,通过select可以监听channel上的数据流动。

select默认是阻塞的,只有当监听的channel中有发送或接收可以进行时才会运行,当多个channel都准备好的时候,select是随机的选择一个执行的。

package main

import "fmt"

func fibonacci(c, quit chan int) {
    x, y := 1, 1
    for {
        select {
        case c <- x:
            x, y = y, x + y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}
ログイン後にコピー

select里面还有default语法,select其实就是类似switch的功能,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)。

select {
case i := <-c:
    // use i
default:
    // 当c阻塞的时候执行这里
}
ログイン後にコピー

超时

有时候会出现goroutine阻塞的情况,那么我们如何避免整个程序进入阻塞的情况呢?我们可以利用select来设置超时,通过如下的方式实现:

func main() {
    c := make(chan int)
    o := make(chan bool)
    go func() {
        for {
            select {
                case v := <- c:
                    println(v)
                case <- time.After(5 * time.Second):
                    println("timeout")
                    o <- true
                    break
            }
        }
    }()
    <- o
}
ログイン後にコピー

runtime goroutine

runtime包中有几个处理goroutine的函数:

  • Goexit

    退出当前执行的goroutine,但是defer函数还会继续调用

  • Gosched

    让出当前goroutine的执行权限,调度器安排其他等待的任务运行,并在下次某个时候从该位置恢复执行。

  • NumCPU

    返回 CPU 核数量

  • NumGoroutine

    返回正在执行和排队的任务总数

  • GOMAXPROCS

    用来设置可以并行计算的CPU核数的最大值,并返回之前的值。

【相关推荐:Go视频教程编程教学

以上がシングルスレッドは Go 言語の機能ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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

ホット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衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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

GOの浮動小数点番号操作に使用されるライブラリは何ですか? GOの浮動小数点番号操作に使用されるライブラリは何ですか? Apr 02, 2025 pm 02:06 PM

GO言語の浮動小数点数操作に使用されるライブラリは、精度を確保する方法を紹介します...

Go's Crawler Collyのキュースレッドの問題は何ですか? Go's Crawler Collyのキュースレッドの問題は何ですか? Apr 02, 2025 pm 02:09 PM

Go Crawler Collyのキュースレッドの問題は、Go言語でColly Crawler Libraryを使用する問題を調査します。 �...

GOのどのライブラリが大企業によって開発されていますか、それとも有名なオープンソースプロジェクトによって提供されていますか? GOのどのライブラリが大企業によって開発されていますか、それとも有名なオープンソースプロジェクトによって提供されていますか? Apr 02, 2025 pm 04:12 PM

大企業または有名なオープンソースプロジェクトによって開発されたGOのどのライブラリが開発されていますか? GOでプログラミングするとき、開発者はしばしばいくつかの一般的なニーズに遭遇します...

GO言語の「VAR」と「タイプ」キーワード定義構造の違いは何ですか? GO言語の「VAR」と「タイプ」キーワード定義構造の違いは何ですか? Apr 02, 2025 pm 12:57 PM

GO言語で構造を定義する2つの方法:VARとタイプのキーワードの違い。構造を定義するとき、GO言語はしばしば2つの異なる執筆方法を見ます:最初...

Goでは、Printlnとstring()関数を備えた文字列を印刷すると、なぜ異なる効果があるのですか? Goでは、Printlnとstring()関数を備えた文字列を印刷すると、なぜ異なる効果があるのですか? Apr 02, 2025 pm 02:03 PM

Go言語での文字列印刷の違い:printlnとstring()関数を使用する効果の違いはGOにあります...

マルチプロセスログを作成するときに、同時性が安全で効率的であることを確認する方法は? マルチプロセスログを作成するときに、同時性が安全で効率的であることを確認する方法は? Apr 02, 2025 pm 03:51 PM

マルチプロセスのログライティングの並行性セキュリティの問題を効率的に処理します。複数のプロセスが同じログファイルを同時に書き込みます。並行性が安全で効率的であることを確認する方法は?これは...

Redisストリームを使用してGO言語でメッセージキューを実装する場合、user_idタイプの変換の問題を解決する方法は? Redisストリームを使用してGO言語でメッセージキューを実装する場合、user_idタイプの変換の問題を解決する方法は? Apr 02, 2025 pm 04:54 PM

redisstreamを使用してGo言語でメッセージキューを実装する問題は、GO言語とRedisを使用することです...

Golang Generic Function Typeの制約がVSCodeで自動的に削除されるという問題を解決する方法は? Golang Generic Function Typeの制約がVSCodeで自動的に削除されるという問題を解決する方法は? Apr 02, 2025 pm 02:15 PM

VSCODEユーザーのGolang Generic Function Typeの制約の自動削除は、VSCODEを使用してGolangコードを書くときに奇妙な問題に遭遇する可能性があります。いつ...

See all articles