LoginSignup
2
0

More than 3 years have passed since last update.

【Go 初心者向け】Concurrency で知っておく必要のある 4 つのこと

Posted at

知っておくべき 4 つのこととは?

  1. Goroutine
  2. Channel
  3. Select
  4. Sync パッケージ

です。

詳しく見ていきましょ。

1. Goroutine

関数やメソッドの前に go を付けると、main() とは違う場所(=スレッド)で処理される

go fmt.Println("別の場所で実行")
go SomeFunction()

main() が終了すると、Goroutine も終了するので、これから説明する方法で「待つ」必要がある。

2. Channel

Goroutine でデータをやり取りをやりとりする仕組み

  • 定義: c := make(chan int)
    • 送信専用も作れる (chan から出ていく矢印): var receveOnly <-chan int
    • 受信専用も作れる (chan に入っていく矢印): var sendOnly chan<- int
  • データ送る: c <- 3
  • データ受け取る: val := <-c
    • データを受け取る準備ができていても、送られてこないと、送られてくるまで待つ
    • ちなみに: 矢印(<-)と c の間にスペースがないのが正しいフォーマット。あっても動作するけど。。
func main() {
    ch := make(chan int)

    fmt.Println("Goroutine 実行するよ!")
    go func() {
        ch <- 42
    }()
    fmt.Println("Goroutine 実行したよ!")

    time.Sleep(2 * time.Second)
    recieved := <-ch
    fmt.Println(recieved)
}

すぐに受け取れなくても、channel を通して送られたデータはその後の処理でちゃんと受け取れる

3. Select

  • 先に実行 (例: 送る、受け取る) できるものを処理する。
  • 何も実行できる状態でなければ、default が実行される。
  • default は書かなくてもよく、もし無ければ、何かが実行できるまで待つ
func main() {

    c1 := make(chan int)
    c2 := make(chan int)

    go func() {
        // 何かしらの処理後に c1 にデータを送る
        time.Sleep(1 * time.Second)
        c1 <- 10

        // 何かしらの処理後に c2 にデータを送る
        time.Sleep(1 * time.Second)
        c2 <- 20
    }()

    // Goroutine を使い無限ループで待機
    go func() {
        for {
            time.Sleep(300 * time.Millisecond)
            select {
            case v1 := <-c1:
                fmt.Printf("recieved %v from c1\n", v1)
            case v2 := <-c2:
                fmt.Printf("recieved %v from c2\n", v2)
            default:
                fmt.Printf("Not Ready !!\n")
            }
        }
    }()

    // 無限ループを 3 秒間だけ回すため、main() で sleep する
    time.Sleep(3 * time.Second)
}
  • time.After(duration) でタイマー的なこともできる

func main() {

    c1 := make(chan int)
    c2 := make(chan int)

    go func() {
        // 何かしらの処理後に c1 にデータを送る
        time.Sleep(1 * time.Second)
        c1 <- 10

        // 何かしらの処理後に c2 にデータを送る
        time.Sleep(1 * time.Second)
        c2 <- 20
    }()

    // time.After を使っているので、main() で無限ループしても、指定時間後に終了する
    for {
        time.Sleep(300 * time.Millisecond)
        select {
        case v1 := <-c1:
            fmt.Printf("recieved %v from c1\n", v1)
        case v2 := <-c2:
            fmt.Printf("recieved %v from c2\n", v2)
        case <-time.After(3 * time.Second):
            fmt.Println("timed out !!")
            return
        }
    }
}

4. Sync パッケージ

  • 定義: var wg sync.WaitGroup
  • 追加: wg.Add(1)
  • 終了: wg.Done()
  • wg.ADD した分 wg.Done するまで、待つ: wg.Wait()
func main() {
    // 定義
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        // 追加
        wg.Add(1)

        go func(c int) {
            // 処理が終わったら終了
            defer wg.Done()

            // 長い処理をしてると仮定
            time.Sleep(2 * time.Second)
            fmt.Println(c)
        }(i)
    }

    // 全て Done するまで待つ
    wg.Wait()
    fmt.Println("Closing...")
}
2
0
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
2
0