0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ゴルーチン間で値を送受信する

プログラムにおいて、値を送信する関数などをProducer、受信する処理をConsumerと呼ぶことがある。
Goで複数のゴルーチンにProducerとConsumerの役割を持たせた場合の並行処理のプログラムについて説明する。

ProducerとConsumerのゴルーチンを作成する

main関数からProducerのゴルーチンをいくつか並行で走らせて実行し。結果をチャネルに入れる。チャネルからConsumerのゴルーチンに渡して、データの処理を実行する。
イメージ図
image.png

使用例としては、様々なサーバからログの解析結果をProducer側で処理し、Consumer側に渡してログの処理や保存をするようなアプリケーションなどがある。

Producer処理の作成

引数iに2をかけるという処理をしてチャネルに送信するproducer関数を作成する。

func producecr(ch chan int, i int) {
    //Something
    ch <- i *2
}

Consumer処理の作成

producer関数のゴルーチンから受け取った値を処理するconsumer関数を作成する。
チャネルの値をrangeで取り出して処理をする。ここでは「process」という文字列と、iに1000を掛けた値を表示する。
また、処理が終わったら、producer関数から渡された値の処理が終わったということを示すためにwg.Done()を実行する。

func consumer(ch chan int, wg *sync.WaitGroup) {
    for i := range ch {
        fmt.Println("process", i*1000)
        wg.Done()
    }
}

main関数の処理

main関数では、まずsync.WaitGroupを宣言する。また、チャネルを作成する。
続いて、producer関数をfor文で10回呼び出す。ループの中でConsumerに値を渡すときにすべてを渡せたかを確認するため、「wg.Add(1)」を書く。その後、ゴルーチンを呼び出してproducer関数を実行する。
次に、consumer関数の引数にチャネルとWaitGroupを渡して実行する。その後、consumer関数がproducer関数から送られてくる値をすべて受け取るまで「wg.Wait()」で待つ。最後に「close(ch)」でチャネルを閉じる。

func main() {
    var wg sync.WaitGroup
    ch:= make(chan int)

    // Produger
    for i :=0; i < 10; i++ {
        wg.Add(1)
        go producer(ch, i)
    }

    // Consumer
    go consumer (ch, &wg)
    wg.Wait()
    close(ch)
}

//以下のように表示される
process 0
process 4000
process 2000
process 6000
process 8000
process 14000
process 10000
process 12000
process 16000
process 18000

producer関数のゴルーチンを呼び出す際に「wg.Add(1)」を10回実行したのでconsumer関数のゴルーチンで「wg.Done()」が10回実行したのでconsumer関数のゴルーチンで「wg.Done()」が10回実行されるまで、main関数の「wg.Wait()」で待ち続ける。

ゴルーチンでrangeがチャネルを待ち続けてしまう場合

consumer関数のゴルーチンでは、チャネルに値が送信されてくるのをrangeで待ち続けてしまうので、main関数の最後に「close(ch)」を実行してチャネルを閉じる。
consumer関数のfor文の外に「fmt.Println("#####")」を書いてみるだけで実行しても「#####」は表示されない。

func consumer(ch chan int, wg *sync.WaitGroup) {
    for i := range ch {
        fmt.Println("process", i*1000)
        wg.Done()
    }
    fmt.Println("#####")
}

これは、consumer関数の「fmt.Println("#####")」を行う前にコードが終了してしまうため。この解消のためにmain関数のconsumerの実行部分のclose(ch)の下に、2秒間待つ処理を書いて、終わったら「Done」と表示させる。

// Consumer
    go consumer (ch, &wg)
    wg.Wait()
    close(ch)
    time.Sleep(2 * time.Second) 
    fmt.Println("Done")

//以下のように表示される
process 0
process 4000
process 2000
process 6000
process 8000
process 14000
process 10000
process 12000
process 16000
process 18000
#####
Done

学習に使用した教材

・『入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』M.A EduTech
https://www.udemy.com/course/golang-webgosql/?utm_medium=udemyads&utm_source=bene-msa&utm_campaign=responsive&utm_content=top-1&utm_term=general&msclkid=81e2f24a32cc185d275d953d60760226&couponCode=NEWYEARCAREERJP

・『シリコンバレー一流プログラマーが教える Goプロフェッショナル大全』酒井 潤 (著)
https://www.amazon.co.jp/%E3%82%B7%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%90%E3%83%AC%E3%83%BC%E4%B8%80%E6%B5%81%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%8C%E6%95%99%E3%81%88%E3%82%8B-Go%E3%83%97%E3%83%AD%E3%83%95%E3%82%A7%E3%83%83%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E5%A4%A7%E5%85%A8-%E9%85%92%E4%BA%95-%E6%BD%A4/dp/4046070897

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?