Goroutineとは
並列処理のこと。他の言語だとマルチスレッドなどと言われる。
49 GoroutineとSync.WaitGroup
normal()とgoroutine()を並列で実行する場合、どちらかのメソッドにgoを頭につけて実行する。
package main
import (
"fmt"
"time"
)
func goroutine(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func normal(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go goroutine("world")
normal("hello")
}
hello
world
world
hello
hello
world
world
hello
hello
ここで、timeをコメントアウトするとnormal()の実行結果しか表示されない。
スレッドを生成し、goroutine()が実行される前にnormal()が終了してしまう
package main
import (
"fmt"
)
func goroutine(s string) {
for i := 0; i < 5; i++ {
// time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func normal(s string) {
for i := 0; i < 5; i++ {
// time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go goroutine("world")
normal("hello")
}
hello
hello
hello
hello
hello
並列処理を実行するには以下のようにする。
package main
import (
"fmt"
"sync"
)
func goroutine(s string, wg *sync.WaitGroup) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
wg.Done()
}
func normal(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main() {
var wg sync.WaitGroup
wg.Add(1)
go goroutine("world", &wg)
normal("hello")
wg.Wait()
}
wgのAdd()で並列処理が一つあると宣言し、Wait()はDone()が実行されるまで終了しないという意味
50 channel
package main
import (
"fmt"
)
func goroutine1(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum
}
func goroutine2(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum
}
func normal(s string) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
}
func main() {
s := []int{1, 2, 3, 4, 5}
c := make(chan int)
go goroutine1(s, c)
go goroutine2(s, c)
x := <-c
fmt.Println((x))
y := <-c
fmt.Println((y))
}
15
15
一つのチャネルを宣言し、goroutine1()とgoroutine2()にわたす。結果をチャネルに入れて帰ってきた値を変数に入れて出力する。
51 Buffered Channel
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)
}
}
channelを作成する際、make()の第2引数に数を指定するとchannelに入れられる数を指定することができる。
指定した数を超えてchannelに値を入れようとするとエラーになる。
また、chennelからforループで取り出す際、close()を実行しておかないとエラーになる。
これは、今回の例でいうと2つの値を取り出した後3つ目を取り出そうとしてしまうため。
53 producerとconsumer
ちょっと理解度浅い
生成したproducerとconsumerの間でchをやり取りし処理を実行している
package main
import (
"fmt"
"sync"
)
func producer(ch chan int, i int) {
ch <- i * 2
}
func consumer(ch chan int, wg *sync.WaitGroup) {
for i := range ch {
func() {
wg.Done()
fmt.Println("process", i*1000)
}()
}
fmt.Println("##################")
}
func main() {
var wg sync.WaitGroup
ch := make(chan int)
// Producer
for i := 0; i < 10; i++ {
wg.Add(1)
go producer(ch, i)
}
// Consumer
go consumer(ch, &wg)
wg.Wait()
close(ch)
}
55 channelとselect
package main
import (
"fmt"
"time"
)
func goroutine1(ch chan string) {
for {
ch <- "packet from 1"
time.Sleep(3 * time.Second)
}
}
func goroutine2(ch chan int) {
for {
ch <- 100
time.Sleep(1 * time.Second)
}
}
func main() {
c1 := make(chan string)
c2 := make(chan int)
go goroutine1(c1)
go goroutine2(c2)
for {
select {
case msg1 := <-c1:
fmt.Println(msg1)
case msg2 := <-c2:
fmt.Println(msg2)
}
}
}
別々のgoroutineで処理を実行し、それらの結果によって分岐させる場合はselectを使う
56 Default selectionとfor break
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
OuterLoop:
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM")
break OuterLoop
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
fmt.Println("###############")
}
outerloopをforの外側に宣言し、breakの後に指定すると、breakでどのループを抜けるかを指定できる
time.Tickはchannelを返す