post Image
Go言語の並行処理デザインパターン by Rob Pike 前編

少し古いですが、Rob Pikeの並行処理デザインパターンのビデオで取り上げられたコードまとめです。
オリジナルのソースコードはこちらで見れます。

Generator ジェネレータ

generator.go
package main

import (
    "fmt"
    "math/rand"
    "time"
    "runtime"
)

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    rand.Seed(time.Now().UnixNano())

    c := boring("boring!") // Function returning a channel.
    for i := 0; i < 5; i++ {
        fmt.Printf("You say: %q\n", <-c)
    }
    fmt.Println("You're boring: I'm leaving.")
}

func boring(msg string) <-chan string {
    c := make(chan string)
    go func() { // We launch the goroutine from inside the function.
        for i := 0; ; i++ {
            c <- fmt.Sprintf("%s %d", msg, i)
            time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
        }

    }()
    return c // Return the channel to the caller.
}

channelはファーストクラスオブジェクトなので、関数の引数や、戻り値に使用できます。
boring関数内でチェネルを作り、goルーチンを使用し、for文の無限ループ内で、メイン関数に送信しています。
チェネルは同期処理なので、メイン側で受信されるまで、boring関数はブロックされます。
boring関数から、チャネルを返却し、メイン関数側へのコールバックに使用します。
乱数で一定時間wait処理を入れ、送受信が同期で処理されているのを分かりやすくしてます。


generator2.go
func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    rand.Seed(time.Now().UnixNano())

    joe := boring("Joe") // Function returning a channel.
    ann := boring("Ann") // Function returning a channel.
    for i := 0; i < 5; i++ {
        fmt.Printf("You say: %q\n", <-joe)
        fmt.Printf("You say: %q\n", <-ann)
    }
    fmt.Println("You're boring: I'm leaving.")
}

func boring(msg string) <-chan string {
// 同じなので省略

この例では、boring関数を2回呼び出し、2つのチャネルを使用しています。boring関数をサービスとして使用しているとのことです。
JoeとAnnがペアで交互に実行されます。Annの方が先に処理を終えても、Joeの処理を待ちます。
次の例では、独立して実行させてみます。


Multiplexing マルチプレクサ

multiplexing.go
func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    rand.Seed(time.Now().UnixNano())

    c := fanIn(boring("Joe"), boring("Ann"))
    for i := 0; i < 10; i++ {
        fmt.Printf("You say: %q\n", <-c)
    }
    fmt.Println("You're boring: I'm leaving.")
}

func fanIn(input1, input2 <-chan string) <-chan string {
    c := make(chan string)
    go func() {
        for {
            c <- <-input1
        }
    }()
    go func() {
        for {
            c <- <-input2
        }
    }()
    return c
}

func boring(msg string) <-chan string {
// 同じなので省略

boring関数のチャネルを2つ引数に取るfanIn関数を定義してます。
内部でgoルーチンを使用して、それぞれ別のgoルーチンで独立して処理するようにしています。
これにより、先に処理が終わった方から、共通のチェネルcに送信するため、JoeとAnnが独立して実行されます。

出力結果

You say: "Joe 0"
You say: "Ann 0"
You say: "Joe 1"
You say: "Ann 1"
You say: "Ann 2"
You say: "Ann 3"
You say: "Ann 4"
You say: "Joe 2"
You say: "Joe 3"
You say: "Ann 5"
You're boring: I'm leaving.

Process finished with exit code 0

Select セレクト

select.go
func fanIn(input1, input2 <-chan string) <-chan string {
    c := make(chan string)

    go func() {
        for {
            select {
            case s := <-input1:
                c <- s
            case s := <-input2:
                c <- s
            }
        }
    }()

    return c
}

先ほどのMultiplexingを、selectで実装したものです。同じ動きになります。
goルーチン1つですっきりと書けます。


Quit? 停止?

quit.go
func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    rand.Seed(time.Now().UnixNano())

    quit := make(chan bool)
    c := boring("Joe", quit)

    for i := rand.Intn(10); i>=0; i-- {
        fmt.Println(<-c)
    }

    quit <- true // send a quit signal after a certain amount of time.
}

func boring(msg string, quit <-chan bool) <-chan string {
    c := make(chan string)
    go func() { // We launch the goroutine from inside the function.
        for i := 0; ; i++ {
            select {
            case c <- fmt.Sprintf("%s %d", msg, i):
            case <-quit:
                return // exit the for-loop.
            }
        }
    }()
    return c // Return the channel to the caller.
}

メイン関数からチェネルをboring関数に渡して、メイン関数からシグナルを送り、boring関数を停止させる例です。
もっともこの例では、メイン関数もすぐに終了してしまうので、次の例にて。。。


RoundTrip ラウンドトリップ

cleanup.go
func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    rand.Seed(time.Now().UnixNano())

    quit := make(chan bool)
    c := boring("Joe", quit)

    for i := rand.Intn(10); i >= 0; i-- {
        fmt.Println(<-c)
    }

    quit <- true // send a quit signal after a certain amount of time.

    fmt.Printf("Joe is done. %t", <-quit)
}

func boring(msg string, quit chan bool) <-chan string {
    c := make(chan string)
    go func() { // We launch the goroutine from inside the function.
        for i := 0; ; i++ {
            select {
            case c <- fmt.Sprintf("%s %d", msg, i):
            case <-quit:
                cleanup()
                quit <- true
                return // exit the for-loop.
            }
        }
    }()
    return c // Return the channel to the caller.
}

func cleanup() {
    // do clean up tasks...
}

quitチャネルを双方向通信で使用します。boring関数にて、リソースのクリーンナップ処理cleanup()をしてから、メイン関数にコールバックしてます。

後編はこちら


『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。