概要

Go言語ではゴルーチン(Goroutine)による並列処理が簡単に出来る。
以下、簡単なサンプル等を記載する。

目次

メイン処理は起動したゴルーチンの終了を待たない

メイン処理は起動した各ゴルーチンの終了を待たない。(他言語でスレッド起動した場合と同様)
例として、それぞれN秒間スリープをするN個の関数をgoルーチンで実行するサンプルを作成してみた。
※メイン処理のスリープ秒数は調整できるようにコマンドライン引数として取得する。

sample_goroutine1.go

package main

import (
    "flag"
    "fmt"
    "strconv"
    "time"
)

func main(){
    printLog("main START")
    mainSleepSec := getSleepSec()
    for i := 1; i <= 5; i++ {
        go sub(i)    // ゴルーチンの生成
    }   
    time.Sleep(time.Second * mainSleepSec)
    printLog("main END")
}

func getSleepSec() time.Duration {
    flag.Parse()
    args := flag.Args()
    if len(args) > 0 { 
        num, _ := strconv.Atoi(args[0])
        return time.Duration(num)
    } else {
        return time.Duration(6)
    }   
}

func sub(no int){
    sleep := time.Duration(no)
    printLog(fmt.Sprintf("sub(%d) START (sleep: %d)", no, sleep))
    time.Sleep(time.Second * sleep)
    printLog(fmt.Sprintf("sub(%d) END", no))
}

func printLog(msg string){
    t := time.Now()
    //fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d.%06d %s\n",
    fmt.Printf("%02d:%02d:%02d.%06d %s\n",
               //t.Year(), t.Month(), t.Day(),
               t.Hour(), t.Minute(), t.Second(),
               t.Nanosecond() / 1000, msg)
}

メイン処理側のスリープを5秒にした場合

$ go run sample_goroutine1.go 6
18:46:26.007975 main START
18:46:26.008156 sub(3) START (sleep: 3)
18:46:26.008169 sub(4) START (sleep: 4)
18:46:26.008187 sub(5) START (sleep: 5)
18:46:26.008195 sub(2) START (sleep: 2)
18:46:26.008252 sub(1) START (sleep: 1)
18:46:27.010345 sub(1) END
18:46:28.011988 sub(2) END
18:46:29.008312 sub(3) END
18:46:30.013348 sub(4) END
18:46:31.012110 sub(5) END
18:46:32.011292 main END

メイン処理側のスリープを2秒にした場合

$ go run sample_goroutine1.go 3
18:47:00.325989 main START
18:47:00.326173 sub(4) START (sleep: 4)
18:47:00.326166 sub(3) START (sleep: 3)
18:47:00.326189 sub(5) START (sleep: 5)
18:47:00.326195 sub(2) START (sleep: 2)
18:47:00.326234 sub(1) START (sleep: 1)
18:47:01.330963 sub(1) END
18:47:02.331014 sub(2) END
18:47:03.330935 sub(3) END
18:47:03.330962 main END

上記の結果から分かる通り、メイン処理は実行したゴルーチンの終了を待たない。

ゴルーチンの終了を待ち合わせる

メイン処理側でゴルーチンの終了を待ち合わせるには sync.WaitGroup を利用する。
※Java等で言うところの join。

sample_goroutine2.go

package main

import (
    "fmt"
    "sync"
    "time"
)

var wait = new(sync.WaitGroup)  // 追加

func main(){
    printLog("main START")
    for i := 1; i <= 5; i++ {
        wait.Add(1)  // 追加
        go sub(i)
    }   
    wait.Wait()  // 追加
    printLog("main END")
}

func sub(no int){
    sleep := time.Duration(no)
    printLog(fmt.Sprintf("sub(%d) START (sleep: %d)", no, sleep))
    time.Sleep(time.Second * sleep)
    printLog(fmt.Sprintf("sub(%d) END", no))
    wait.Done()  // 追加
}

func printLog(msg string){
    t := time.Now()
    fmt.Printf("%02d:%02d:%02d.%06d %s\n",
               t.Hour(), t.Minute(), t.Second(),
               t.Nanosecond() / 1000, msg)
}

実行結果

$ go run sample_goroutine2.go 
18:52:51.336324 main START
18:52:51.336491 sub(5) START (sleep: 5)
18:52:51.336517 sub(3) START (sleep: 3)
18:52:51.336534 sub(2) START (sleep: 2)
18:52:51.336549 sub(4) START (sleep: 4)
18:52:51.336589 sub(1) START (sleep: 1)
18:52:52.340077 sub(1) END
18:52:53.341676 sub(2) END
18:52:54.337123 sub(3) END
18:52:55.341728 sub(4) END
18:52:56.339499 sub(5) END
18:52:56.339582 main END

ゴルーチンと値をやり取りする

チャンネルを使用してゴルーチンと値のやり取りが出来る。
チャンネルは「変数 := make(chan データ型)」で生成する事ができる。

sample_goroutine3.go

package main

import (
    "fmt"
    "sync"
    "time"
)

var wait = new(sync.WaitGroup)

func main(){
    printLog("main START")
    var channels []chan string
    for i := 1; i <= 5; i++ {
        // チャンネル生成
        ch := make(chan string)
        channels = append(channels, ch) 
        wait.Add(1)
        go sub(i, ch) 
    }   
    wait.Wait()
    for i, ch := range channels {
        // チャンネルから値を受信
        message := <-ch
        fmt.Printf("message[%d]: %s\n", i, message)
    }   
    printLog("main END")
}

func sub(no int, ch chan string){
    sleep := time.Duration(no)
    printLog(fmt.Sprintf("sub(%d) START (sleep: %d)", no, sleep))
    time.Sleep(time.Second * sleep)
    printLog(fmt.Sprintf("sub(%d) END", no))
    wait.Done()
    // チャンネルに値を送信
    ch <- fmt.Sprintf("sub(%d) finished!", no) 
}

func printLog(msg string){
    t := time.Now()
    fmt.Printf("%02d:%02d:%02d.%06d %s\n",
               t.Hour(), t.Minute(), t.Second(),
               t.Nanosecond() / 1000, msg)
}

実行結果

$ go run sample_goroutine3.go
19:29:13.767172 main START
19:29:13.767313 sub(5) START (sleep: 5)
19:29:13.767344 sub(3) START (sleep: 3)
19:29:13.767354 sub(4) START (sleep: 4)
19:29:13.767358 sub(2) START (sleep: 2)
19:29:13.767414 sub(1) START (sleep: 1)
19:29:14.770515 sub(1) END
19:29:15.772479 sub(2) END
19:29:16.768126 sub(3) END
19:29:17.771446 sub(4) END
19:29:18.772457 sub(5) END
message[0]: sub(1) finished!
message[1]: sub(2) finished!
message[2]: sub(3) finished!
message[3]: sub(4) finished!
message[4]: sub(5) finished!
19:29:18.772529 main END

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2020-01-05 (日) 19:32:09 (259d)