LoginSignup
9
6

More than 3 years have passed since last update.

【Golang】Go言語 channel(チャネル)について

Posted at

channelとは?

Goroutineは並行実行されているので、通常だとデータのやりとりができません。
しかし、channelというのををつかうとデータのやりとりができます。

トンネルをつなぐようなイメージです!


package main

import (
    "fmt"
)

func goroutine1(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    //チャネルにデータを送信
    c <- sum
}

func main() {
    s := []int{1, 2, 3, 4}
    c := make(chan int)
    go goroutine1(s, c)
      //送られてきたデータを受信
    p := <-c
    fmt.Println(p) // => 10
}

c <- sum でcに値を送信し、p := <-c で受信しています。ここでは受信するまでブロッキングされていて、pはsumを受信するまで待っている状態です。sync.Waitと同じような状態ですね!

Buffered channels

バッファとはプログラムで処理を行なうときに、データを一時的に格納するためのメモリー領域のことをいいます。
チャネルに一時データを保存するイメージです。


package main

import "fmt"

func main() {
    //2つまでバッファを保存できる
    ch := make(chan int, 2)
    ch <- 100
    //len()でチャネルの長さを調べられる
    fmt.Println(len(ch))
    ch <- 200
    fmt.Println(len(ch))
    //3つめのデータを送信しようとするとエラーになる
    ch <- 300
    fmt.Println(len(ch)) // => fatal error: all goroutines are asleep - deadlock!
}
実行結果
1
2
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /tmp/sandbox795806942/prog.go:11 +0x16f

2つまでしか保存できないので、3つめを送信しようとすると当然ですがエラーになります。(デッドロック)

↓にように手前で一個チャネルを取りだしてから300を送信すれば、エラーにならなくなります。


func main() {
    ch := make(chan int, 2)
    ch <- 100
    fmt.Println(len(ch))
    ch <- 200
    fmt.Println(len(ch))

    x := <-ch
    fmt.Println(x)
    ch <- 300
    fmt.Println(len(ch))
}
実行結果
1
2
100
2

次に以下のコードをご覧ください↓


package main

import "fmt"

func main() {
    ch := make(chan int, 2)
    ch <- 100
    fmt.Println(len(ch))
    ch <- 200
    fmt.Println(len(ch))

    for c := range ch {
        fmt.Println(c)
    }
}

これはチャネルをrangeでループさせようとしているのですが、これはエラーになります。

実行結果
1
2
100
200
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /tmp/sandbox892357489/prog.go:12 +0x1f3

これは存在しない3つ目のチャネルまでループしようとしてしまうからです。
2つ目のチャネルでループ処理を止めるにはチャネルを一旦クローズする必要があります。
そのためにはclose(ch)を記述する必要があります。


package main

import "fmt"

func main() {
    ch := make(chan int, 2)
    ch <- 100
    fmt.Println(len(ch))
    ch <- 200
    fmt.Println(len(ch))
    close(ch)
    for c := range ch {
        fmt.Println(c)
    }
}
実行結果
1
2
100
200

最後まで読んでいただきありがとうございます!

ご指摘などございましたら、コメントいただけると幸いです!!

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6