雑なメモ書き

気楽にいきます

Goの並行処理を実際に試す(3)

引き続きパターンを試す。

1-9-timeout-direct-select.go

// Global timer returns after 5 seconds, stopping execution in select control block

  • global timerを使用して5秒後にblockをtime outさせる
//    Kevin Chen (2017)
// Patterns from Pike's Google I/O talk, "Go Concurrency Patterns"

//  Global timer returns after 5 seconds, stopping execution in select control block

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := generator("Hi!")
    timeout := time.After(5 * time.Second)
    for i := 0; i < 10; i++ {
        select {
        case s := <-ch:
            fmt.Println(s)
        case <-timeout: // time.After returns a channel that waits N time to send a message
            fmt.Println("5s Timeout!")
            return
        }
    }
}

func generator(msg string) <-chan string { // returns receive-only channel
    ch := make(chan string)
    go func() { // anonymous goroutine
        for i := 0; ; i++ {
            ch <- fmt.Sprintf("%s %d", msg, i)
            time.Sleep(time.Second)
        }
    }()
    return ch
}

2-1-quit-select.go

// Deterministically quit goroutine with quit channel option in select

  • generatorに設定したオプションでgoroutineを終わらせることが出来る
//    Kevin Chen (2017)
// Patterns from Pike's Google I/O talk, "Go Concurrency Patterns"

//  Deterministically quit goroutine with quit channel option in select

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    quit := make(chan bool)
    ch := generator("Hi!", quit)
    for i := rand.Intn(50); i >= 0; i-- {
        fmt.Println(<-ch, i)
    }
    quit <- true
}

func generator(msg string, quit chan bool) <-chan string { // returns receive-only channel
    ch := make(chan string)
    go func() { // anonymous goroutine
        for {
            select {
            case ch <- fmt.Sprintf("%s", msg):
                // nothing
            case <-quit:
                fmt.Println("Goroutine done")
                return
            }
        }
    }()
    return ch
}

2-2-receive-quit.go

// Deterministically quit goroutine with quit channel option in select

  • さっきと同様にoptionを渡してgroutineを終わらせている
//    Kevin Chen (2017)
// Patterns from Pike's Google I/O talk, "Go Concurrency Patterns"

//  Deterministically quit goroutine with quit channel option in select

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    quit := make(chan string)
    ch := generator("Hi!", quit)
    for i := rand.Intn(10); i >= 0; i-- {
        fmt.Println(<-ch, i)
    }
    quit <- "Bye!"
    fmt.Printf("Generator says %s", <-quit)
}

func generator(msg string, quit chan string) <-chan string { // returns receive-only channel
    ch := make(chan string)
    go func() { // anonymous goroutine
        for {
            select {
            case ch <- fmt.Sprintf("%s", msg):
                // nothing
            case <-quit:
                quit <- "See you!"
                return
            }
        }
    }()
    return ch
}

2-3-daisy-chain.go

// Daisy chaining goroutines... all routines at once, so if one is fulfilled // everything after should also have their blocking commitment (input) fulfilled

  • channnelを使用して先に走らせたgoroutineで計算
  • rightが書き込まれるまでblockされている
//    Kevin Chen (2017)
// Patterns from Pike's Google I/O talk, "Go Concurrency Patterns"

//  Daisy chaining goroutines... all routines at once, so if one is fulfilled
//  everything after should also have their blocking commitment (input) fulfilled

package main

import (
    "fmt"
)

// takes two int channels, stores right val (+1) into left
func f(left, right chan int) {
    left <- 1 + <-right // bafter 1st right read, locks until left read
}

func main() {
    const n = 10000

    // construct an array of n+1 int channels
    var channels [n + 1]chan int
    for i := range channels {
        channels[i] = make(chan int)
    }

    // wire n goroutines in a chain
    for i := 0; i < n; i++ {
        go f(channels[i], channels[i+1])
    }

    // insert a value into right-hand end
    go func(c chan<- int) { c <- 1 }(channels[n])

    // get value from the left-hand end
    fmt.Println(<-channels[0])
}

2-4-google-search.go

// Using go patterns with experiment 'Google Search' i.e concurrent goroutines getting results

  • Google Searchぽいことを体感させるぽい
//  Kevin Chen (2017)
//  Patterns from Pike's Google I/O talk, "Go Concurrency Patterns"

//  Using go patterns with experiment 'Google Search' i.e concurrent goroutines getting results

package main

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

var (
    Web    = fakeSearch("web")
    Web2   = fakeSearch("web")
    Image  = fakeSearch("image")
    Image2 = fakeSearch("image")
    Video  = fakeSearch("video")
    Video2 = fakeSearch("video")
)

type Result string
type Search func(query string) Result

func main() {
    rand.Seed(time.Now().UnixNano())
    start := time.Now()
    results := Google("golang") // collate results
    elapsed := time.Since(start)
    fmt.Println(results)
    fmt.Println(elapsed)
}

func fakeSearch(kind string) Search {
    return func(query string) Result {
        time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
        return Result(fmt.Sprintf("%s result for %q\n", kind, query))
    }
}

func First(query string, replicas ...Search) Result {
    c := make(chan Result)
    searchReplica := func(i int) { c <- replicas[i](query) }
    for i := range replicas {
        go searchReplica(i)
    }
    return <-c
}

func Google(query string) (results []Result) {
    c := make(chan Result)
    go func() { c <- First(query, Web, Web2) }()
    go func() { c <- First(query, Image, Image2) }()
    go func() { c <- First(query, Video, Video2) }()

    timeout := time.After(80 * time.Millisecond)

    for i := 0; i < 3; i++ {
        select {
        case result := <-c:
            results = append(results, result)
        case <-timeout:
            fmt.Println("timed out")
            return
        }
    }
    return
}

一応乗っかってるの全部できた。