ホームページ >バックエンド開発 >Golang >Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

Go语言进阶学习
Go语言进阶学习転載
2023-07-21 10:09:541492ブラウズ
チャネルが必要な理由

Go のチャネル (パイプラインとも呼ばれます) は、複数のスレッド 間でデータ # を共有するために使用されます。 通常、Go での共有データも channel

を使用しますが、データを共有する方法は 2 つあります。 # 共有メモリは通信を実装します。

  • #チャネルを通じてコミュニケーションします (推奨)。

#共有メモリ通信が推奨されないのはなぜですか?

サンプル コード: マルチスレッドは値を変更します。

#関数

func Calc() {
    defer wg.Done()
    NUM = NUM - 1
}

main

var NUM = 100
var wg sync.WaitGroup


func main() {
    for i := 0; i<100;i++  {
        wg.Add(1)
        go Calc()
}
    wg.Wait()
    fmt.Println(NUM)
}

実行結果

Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

#はい、2です、あなたは混乱しています、ははは、理論は0であるはずです、ああ。 ###############どうしてこれなの?


これが、共有メモリが推奨されない理由です。コードはすでにマルチスレッドになっています。

#最初の関数コード

の 3 行目、

NUM = NUM​​ - 1

# ####。

如果多个线程同时执行到这一行,并且没有加锁,就会出现数据错乱。

那该怎么做呢?

加锁,加锁可以保证某一段代码只能被一个线程执行,防止被争抢。

代码

func Calc() {
    defer wg.Done()
    mutex.Lock()
    NUM = NUM - 1
    mutex.Unlock()
}

第3行加锁,第5行解锁

执行结果

Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

这次真的是0的,不管执行几次。

但是会发现一个问题,如果采用这种方式,需要常常注意竞争问题。

所以不是太推荐,需要考虑的比较多,并且各种加锁会消耗性能。

channel语法

channel格式

var 变量名 chan 类型
例如
var x1 chan int //x1管道里面只能存int类型数据
var x2 chan string //x2管道里面只能存字符串类型数据

注意

Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

定义管道时,chan int是一个整体,别搞错了各位。

创建channel

创建channel,只能通过make创建。

格式

var 变量名 = make(chan 类型,[管道大小])
示例
var chan1 = make(chan int,10)//管道可以放10个int元素
var chan2 = make(chan string,5)//管道可以放5个string元素

channel操作

创建一个管道。

ch = make(chan int,10)

channel是一个管道,就像一个管子。

所以可以像管子里面塞东西,并且能取东西关闭管道就是这个管道不能用了,里面的值取完就打样了

像管子塞东西(发送)ch <- 666

从管子取东西(接收)var x = <- ch

チューブを閉じてくださいclose(ch)

#注: チャンネルは 先入れ先出し 構造はこのようになります。 ########################予防:####################### #チャンネルがいっぱいの場合、再度ブロックされるとブロックされます。

Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

#チャネルが閉じられている場合、それ以上値を追加することはできません。そうでない場合はパニックが発生します。


#チャネルが閉じていても、パイプの値が完全に取得されるまでは値を取得できます。値を取得した後は、ゼロが対応する型の値が取得されます。

  • #パイプを繰り返し閉じることはできません。繰り返し閉じるとパニックが発生します。

  • #バッファなしパイプ
  • バッファなしとは、パイプに長さが存在しないことを意味します。このような。

    就像快读员没有快递柜,需要直接将快递给客户,如果没人要就撂摊子。

    Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

    示例代码

    package main
    
    
    import (
        "fmt"
    )
    
    
    //模拟张三
    func 张三(x chan string) {
        var a = <-x
        fmt.Println(a)
    }
    
    
    func main() {
        //通道没有长度,就是无缓冲通道
        var x = make(chan string)
        go 张三(x)
        x <- "张三的快递"
        fmt.Println("张三快递交付成功")
    }

    第16行写入一个值,同理,张三就要等着去接,如果没人接,那就完了。

    假设注释第9行代码。

    Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

    直接报错,all goroutines are asleep - deadlock!,这句话的意思是所有的协程都睡着了,死锁

    无缓冲说明通道长度为0发送一个值会阻塞住

    这就相当于快递员直接找张三,但是张三没了,但是快递员还得一直等着,等等等,然后挂了,终究还是没送出去。

    有缓冲管道

    Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

    这个就简单啦,多了一个快递柜,快递员直接将快递仍快递柜就行了。

    示例代码

    package main
    
    
    import (
        "fmt"
        "sync"
    )
    
    
    var wg sync.WaitGroup
    
    
    //快递员,快递员放10个快递
    func 快递员(kuaidigui chan string) {
        defer wg.Done()
        for i := 0; i < 10; i++ {
            fmt.Println("快递员放入了第",i,"快递")
            kuaidigui <- fmt.Sprintf("第%d个快递", i)
    }
        //放完快递就关闭了通道
        close(kuaidigui)
    }
    
    
    //张三,拿走3个快递
    func 张三(kuaidigui chan string) {
        defer wg.Done()
        for i := 0; i < 3; i++ {
            fmt.Println("张三拿走" + <-kuaidigui)
    }
    }
    //李四拿走7个快递
    func 李四(kuaidigui chan string) {
        defer wg.Done()
        for i := 0; i < 7; i++ {
            fmt.Println("李四拿走" + <-kuaidigui)
    }
    }
    func main() {
        //快递柜,10个大小
        var 快递柜 = make(chan string, 10)
        wg.Add(3)
        go 快递员(快递柜)
        go 张三(快递柜)
        go 李四(快递柜)
        wg.Wait()
    }

    执行结果

    Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

    遍历channel两种方式

    代码

    func main() {
        //快递柜,10个大小
        var ch = make(chan int, 10)
        //向管道中发送值
        for i := 0; i < 10; i++ {
            ch <- i
    }
        //方式一取值
        //for {
        //i, ok := <-ch
        ////取完值ok就是false
        //if !ok {
        //      //结束循环
        //      break
        //}
        //fmt.Println(i)
        //}
        //方式二取值
        for i:=range ch{
            fmt.Println(i)
    }
    }

    执行结果

    Go 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事

    报错是因为我在main中完成了发送值和取值两个操作,所以会出现上述问题,但是结果是没有错的。

    一方向チャネル

    チャネルが可能であることはわかっています 値の送信 は値 を受け取りますが、理論的には安全上の理由から一部のシナリオでは値を取得するだけで、後者は値を送信することしかできません。

    一方向チャネルは通常、関数パラメータにのみ反映されます。

    • 仮パラメータ chan<- chan type書き込み専用。

    • #仮パラメータ <-chan chan type読み取り専用。

    #上記の配送業者コードを変更します。

    package main
    
    
    import (
        "fmt"
        "sync"
    )
    
    
    var wg sync.WaitGroup
    
    
    //快递员,快递员放10个快递,只写 chan<- string
    func 快递员(kuaidigui chan<- string) {
        defer wg.Done()
        for i := 0; i < 10; i++ {
            fmt.Println("快递员放入了第", i, "快递")
            kuaidigui <- fmt.Sprintf("第%d个快递", i)
    }
        //放完快递就关闭了通道
        close(kuaidigui)
    }
    
    
    //张三,拿走3个快递,只读<-chan string
    func 张三(kuaidigui <-chan string) {
        defer wg.Done()
        for i := 0; i < 3; i++ {
            fmt.Println("张三拿走" + <-kuaidigui)
    }
    }
    
    
    //李四拿走7个快递
    func 李四(kuaidigui <-chan string) {
        defer wg.Done()
        for i := 0; i < 7; i++ {
            fmt.Println("李四拿走" + <-kuaidigui)
    }
    }
    func main() {
        //快递柜,10个大小
        var 快递柜 = make(chan string, 10)
        wg.Add(3)
        go 快递员(快递柜)
        go 张三(快递柜)
        go 李四(快递柜)
        wg.Wait()
    }

    总结

    上述讲述了Go语言并发如何和channel配合使用,毕竟我们一般的任务都不是单独运行的,都是互相配合的。

    我们讲述了如何创建channel如何使用channel有缓冲管道和无缓冲管道区别,并且拒了一个快递员例子来展示协程和channel如何配合,最后用单向通道又加固了一下代码。

    我的代码中使用了中文命名变量名是为了好看,实际开发中千万不要这样!!!

以上がGo 言語の同時実行性 (チャネル) の基本を理解するのに役立つ記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はGo语言进阶学习で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。