例によって並行プログラミングを行きます

私の独立したブログより転載:https://liushiming.cn/2020/01/21/concurrent-programming-in-go-by-examples/

アウトライン

並行性はどこへ行くの開発者を誘致するための機能であり、それが困難であり、以下の自己組織化、企業内で共有して習得についてのランニングに興味がある、すべてを例に説明します。

コードは、ここにGitHubのそこに。

パラレル対同時

並列処理(並列)の違い対同時(並行性)
あなたは、携帯電話を半分の食事を食べ、そしてあなただけが同時サポートしませ並列をサポートしていないことを、食べた後、ピックアップする必要があります。
それは同時実行のためのあなたのサポートを示し、食事の後に続けて、その後、あなたは、携帯電話を半分の食事を食べ、そしてあなたは、携帯電話を停止し拾います。
サイドを食べながら、あなたはそれがあなたのサポートパラレルを示し、半分の食事、電話、およびお使いの携帯電話を食べます。

golang支持体は平行と同時、すなわち、(指定されたCPU runtime.GOMAXPROCS(N INTすべてデフォルト))を同時に並列動作のCPU、複数の

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    for i := 0; i < 10; i++ {
        go func(i int) {
            fmt.Println("i: ", i)
        }()
    }
    time.Sleep(1 * time.Second)
}

ゴルーチン

ただ、ゴルーチンを開始するには、キーワード、外出先を使用し、それが非同期非ブロック方式を使用して実行されます。

// goroutines/example1 
package main

import (
    "fmt"
    "time"
)

func main(){
    go SayHello()

    fmt.Println("hello from main")
    time.Sleep(time.Second)
}

func SayHello(){
    fmt.Println("hello from goroutine")
}

ゴルーチン消費

// goroutine-benchmark/goroutine-memory
package main

import (
    "fmt"
    "runtime"
    "sync"
)

func main() {
    memConsumed := func() uint64 {
        runtime.GC()
        var s runtime.MemStats
        runtime.ReadMemStats(&s)
        return s.Sys
    }

    var c <-chan interface{}
    var wg sync.WaitGroup
    noop := func() { wg.Done(); <-c } // a goroutine which will never exit, because it's waiting for channel c all the time
    const numGoroutines = 1e5
    wg.Add(numGoroutines)
    before := memConsumed()
    for i := numGoroutines; i > 0; i-- {
        go noop()
    }
    wg.Wait()
    after := memConsumed()
    fmt.Printf("%.3fkb", float64(after-before)/numGoroutines/1024)
}

無名関数

// goroutines/example2 
package main

import (
    "fmt"
)

func main(){
    go func(){
        fmt.Println("hello from goroutine")
    }()

    fmt.Println("hello from main")
}

機能第一級の市民

// goroutines/example3  
package main

import (
    "fmt"
)

func main() {
    sayHello := func() {
        fmt.Println("hello from goroutine")
    }

    go sayHello()

    fmt.Println("hello from main")
}

クロージャ

// goroutine-and-closure/simple-closure  
package main

import (
    "fmt"
)

func main() {
    greeting := "hello"

    go func() {
        greeting = "welcome"
    }()

    fmt.Println(greeting)
}

クロージャ - フィボナッチ数

package main

import (
    "fmt"
)

func main() {
    fibonacci := func() func() int {
        back1, back2 := -1, 1
        return func() int {
            back1, back2 = back2, (back1 + back2)
            return back2
        }
    }

    f := fibonacci()

    for i := 0; i < 10; i++ {
        fmt.Println(f())
    }
}

クロージャの一般的なエラー

package main

import (
    "fmt"
    "time"
)

func main() {
    for _, greeting := range []string{"hello", "greetings", "good day"} {
        go func() {
            fmt.Println(greeting)
        }()
    }

    time.Sleep(1 * time.Second)
}

避けるために、一般的な間違いを閉鎖

package main

import (
    "fmt"
    "time"
)

func main() {
    for _, greeting := range []string{"hello", "greetings", "good day"} {
        go func(words string) {
            fmt.Println(words)
        }(greeting)
    }

    time.Sleep(1 * time.Second)
}

レース条件

以下の結果を実行しているいくつかのプログラムがありますか?

// race-condition/simplest-race-condition 
package main

import (
    "fmt"
)

func main(){
    var data int

    go func(){
        data++
    }()

    if data == 0{
        fmt.Printf("the value is %v.\n",data)
    }
}

実行中のプログラムの三つの可能な結果があります。

  1. 前に14行を、ライン11(データ++)を行い、印刷されていないどのよう、プログラムを終了
  2. 、ライン14、ライン「値が0である」印刷され、そして15 11実行前に
  3. 、ライン11及びライン14を実行するが、行ライン11が実行15「の値が1である」印刷されます

ミューテックス

package main

import (
    "fmt"
    "sync"
)

func main(){
    var lock sync.Mutex

    var data int
    go func() {
        lock.Lock()
        data++
        lock.Unlock()
    }()

    lock.Lock()
    if data == 0 {
        fmt.Printf("the value is %v.\n", data)
    } else {
        fmt.Printf("the value is %v.\n", data)
    }
    lock.Unlock()
}

例1:ウェブサイトのヘルスチェック

機能:サイトの正常数が訪問したかどうかをチェック

package main

import (
    "fmt"
    "net/http"
)

func main() {
    links := []string{
        "http://qq.com",
        //"http://google.com",
        "http://taobao.com",
        "http://baidu.com",
        "http://z.cn",
        "http://great.website",
    }

    for _, link := range links {
        checkLink(link)
    }
}

func checkLink(link string) {
    _, err := http.Get(link)
    if err != nil {
        fmt.Println(link, "might be down!", err)
        return
    }
    fmt.Println(link, "is up!")
}

質問:サイトアクセスのタイムアウト(google.comコメントオープン)のセクションがある場合は、プログラムが起こるのだろうか?このような事態を回避するには?

示例2:网站健康检查(goroutine)

功能:同时检查一批网站是否能正常访问

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    links := []string{
        "http://qq.com",
        "http://google.com",
        "http://taobao.com",
        "http://baidu.com",
        "http://z.cn",
        "http://great.website",
    }

    for _, link := range links {
        go checkLink(link)
    }

    time.Sleep(5 * time.Second)
}

func checkLink(link string) {
    _, err := http.Get(link)
    if err != nil {
        fmt.Println(link, "might be down! ", err)
        return
    }
    fmt.Println(link, "is up!")
}

问题1:输出的结果是什么?
问题2:如果把sleep拿走,结果会怎么样?为什么?

waitgroup

// wait-group/helloworld-waitgroup 
package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            fmt.Println(i)
        }(i)
    }
    wg.Wait()
    
    fmt.Println("All goroutines complete.")
}

示例3:网站健康检查(waitgroup)

package main

import (
    "fmt"
    "net/http"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    links := []string{
        "http://qq.com",
        //"http://google.com",
        "http://taobao.com",
        "http://baidu.com",
        "http://z.cn",
        "http://great.website",
    }

    for _, link := range links {
        wg.Add(1)
        go checkLink(&wg, link)
    }

    wg.Wait()

}

channel

package main

import (
    "time"
)

func main() {
    var ch chan string     // 声明
    ch = make(chan string) // 初始化

    go ask(ch)
    go answer(ch)

    time.Sleep(1 * time.Second)
}

func ask(ch chan string) {
    ch <- "what's your name?"
}

func answer(ch chan string) {
    println("he asked: ", <-ch)
    println("My name is Shiming")
}

示例4:网站健康检查(channel)

// channels/unbuffered
package main

import (
    "fmt"
    "net/http"
    "time"
)

// 检查网站是否正常
func main() {
    links := []string{
        "http://baidu.com",
        "http://qq.com",
        "http://taobao.com",
        "http://jd.com",
        "http://z.cn",
    }

    c := make(chan string)

    for _, link := range links {
        go func(link string) {
            checkLink(link, c)
        }(link)
    }

    for l := range c {
        go func(link string) {
            time.Sleep(5 * time.Second)
            checkLink(link, c)
        }(l)
    }
}

func checkLink(link string, c chan string) {
    defer func() { c <- link }()

    _, err := http.Get(link)

    if err != nil {
        fmt.Println(link, "might be down!")
    } else {
        fmt.Println(link, "is up!")
    }

}

带方向的channel

// channels/channeldirection
package main

import "fmt"

// This `ping` function only accepts a channel for sending
// values. It would be a compile-time error to try to
// receive on this channel.
func ping(pings chan<- string, msg string) {
    pings <- msg
}

// The `pong` function accepts one channel for receives
// (`pings`) and a second for sends (`pongs`).
func pong(pings <-chan string, pongs chan<- string) {
    msg := <-pings
    pongs <- msg
    // pongs <- <-pings
}

func main() {
    pings := make(chan string, 1)
    pongs := make(chan string, 1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}

带缓冲区的channel

// channels/simplejobqueue
package main

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

type Job struct {
    JobName string
}

func worker(jobChan <-chan Job) {
    for job := range jobChan {
        process(job)
    }
}

func process(j Job) {
    fmt.Println(j.JobName + " processed")
}

func main() {
    // make a channel with a capacity of 100.
    jobChan := make(chan Job, 100)

    // start the worker
    go worker(jobChan)

    s := rand.NewSource(time.Now().UnixNano())
    r := rand.New(s)
    // enqueue a job
    for {
        i := r.Intn(10000)
        time.Sleep(time.Second)
        fmt.Printf("job %d assigned\n", i)
        job := Job{JobName: fmt.Sprintf("job " + strconv.Itoa(i))}
        jobChan <- job
    }

}

关闭通道

// channels/simpleworkpoll/unsafe 
package main

import (
    "fmt"
)

// Here's the worker, of which we'll run several
// concurrent instances. These workers will receive
// work on the `jobs` channel and send the corresponding
// results on `results`. We'll sleep a second per job to
// simulate an expensive task.
func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}

func main() {

    // In order to use our pool of workers we need to send
    // them work and collect their results. We make 2
    // channels for this.
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // This starts up 3 workers, initially blocked
    // because there are no jobs yet.
    for i := 0; i < 3; i++ {
        go worker(i, jobs, results)
    }

    // Here we send 5 `jobs` and then `close` that
    // channel to indicate that's all the work we have.
    for j := 0; j < 5; j++ {
        jobs <- j
    }
    close(jobs)

    // warning: it's not a good way to get the result
    for r := 0; r < 5; r++ {
        fmt.Println(<-results)
    }

}

关闭/range通道(正确示范)

// channels/simpleworkpoll/safe
package main

import (
    "fmt"
    "sync"
)

// Here's the worker, of which we'll run several
// concurrent instances. These workers will receive
// work on the `jobs` channel and send the corresponding
// results on `results`. We'll sleep a second per job to
// simulate an expensive task.
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}

func main() {

    // In order to use our pool of workers we need to send
    // them work and collect their results. We make 2
    // channels for this.
    jobs := make(chan int, 100)
    results := make(chan int, 100)
    wg := new(sync.WaitGroup)

    // This starts up 3 workers, initially blocked
    // because there are no jobs yet.
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go worker(i, jobs, results, wg)
    }

    // Here we send 5 `jobs` and then `close` that
    // channel to indicate that's all the work we have.
    for j := 0; j < 5; j++ {
        jobs <- j
    }
    close(jobs)

    go func(wg *sync.WaitGroup, results chan int) {
        fmt.Println("waiting")
        wg.Wait() // GOOD
        fmt.Println("done waiting")
        close(results)
    }(wg, results)

    //for r := 0; r < 5; r++ {
    //  fmt.Println(<-results)
    //}
    // Finally we collect all the results of the work.
    for r := range results {
        fmt.Println(r)
    }
}

非阻塞channel - select

// channels/select 
package main

import (
    "fmt"
    "time"
)

func main() {
    stop := make(chan bool)

    go func() {
        for {
            select {
            case <-stop:
                fmt.Println("监控退出,停止了...")
                return
            default:
                fmt.Println("goroutine监控中...")
                time.Sleep(2 * time.Second)
            }
        }
    }()

    time.Sleep(10 * time.Second)
    fmt.Println("可以了,通知监控停止")
    stop <- true
    //为了检测监控过是否停止,如果没有监控输出,就表示停止了
    time.Sleep(5 * time.Second)

}

timeout

// channels/select-timeout
package main

import (
    "fmt"
    "time"
)

func main() {
    var msg string
    ch := make(chan string, 1)
    defer close(ch)

    go func() {
        //time.Sleep(1 * time.Microsecond)   // uncomment to timeout
        ch <- "hi"
    }()

    select {
    case msg = <-ch:
        fmt.Println("Read from ch:", msg)
    case <-time.After(1 * time.Microsecond):
        fmt.Println("Timed out")
    }
}

过载保护

// channels/selectjobqueue/waitgroup
package main

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

var wg sync.WaitGroup
var workDone int
var workAssign int

type Job struct {
    JobName string
}

func worker(i int, jobChan <-chan Job) {
    defer wg.Done()
    for job := range jobChan {
        process(job)
        fmt.Println("worker: " + strconv.Itoa(i) + " " + job.JobName + " processed")
        workDone++
    }
}

func process(j Job) {
    // work to process
}

// TryEnqueue tries to enqueue a job to the given job channel. Returns true if
// the operation was successful, and false if enqueuing would not have been
// possible without blocking. Job is not enqueued in the latter case.
func TryEnqueue(job Job, jobChan chan<- Job) bool {
    select {
    case jobChan <- job:
        return true
    default:
        return false
    }
}

func main() {
    //runtime.GOMAXPROCS(runtime.NumCPU())

    // make a channel with a capacity of 10.
    jobChan := make(chan Job, 10)

    // start the worker
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(i int) {
            worker(i, jobChan)
        }(i)

    }

    s := rand.NewSource(time.Now().UnixNano())
    r := rand.New(s)
    // enqueue a job
    start := time.Now()
    for {
        i := r.Intn(10000)
        job := Job{JobName: fmt.Sprintf("job " + strconv.Itoa(i))}
        if !TryEnqueue(job, jobChan) {
            fmt.Println("max capacity reached, try later")
            close(jobChan)
            break
        } else {
            fmt.Printf("job %d assigned\n", i)
            workAssign++
        }
    }
    elapsed := time.Since(start)
    fmt.Printf("%d work assigned, %d been done, in %s", workAssign, workDone, elapsed)
}

示例5:google search 1.0

// googlesearch/googlesearch1.0
/*
Example: Google Search
Given a query, return a page of search results (and some ads).
Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results.
Google function takes a query and returns a slice of Results (which are just strings)
Google invokes Web, Image and Video searches serially, appending them to the results slice.
Run each search in series
*/
package main

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

var (
    web   = fakeSearch("web")
    image = fakeSearch("image")
    video = fakeSearch("video")
)

type (
    result string
    search func(query string) result
)

func main() {
    rand.Seed(time.Now().UnixNano())

    start := time.Now()
    results := google("golang")
    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 google(query string) (results []result) {
    results = append(results, web(query))
    results = append(results, image(query))
    results = append(results, video(query))

    return results
}

示例6:google search 2.0

// googlesearch/googlesearch2.0
/*
Example: Google Search
Given a query, return a page of search results (and some ads).
Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results.
Run the Web, Image and Video searches concurrently, and wait for all results.
No locks. No condition variables. No callbacks
Run each search in their own Goroutine and wait for all searches to complete before display results
*/
package main

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

var (
    web   = fakeSearch("web")
    image = fakeSearch("image")
    video = fakeSearch("video")
)

type (
    result string
    search func(query string) result
)

func main() {
    rand.Seed(time.Now().UnixNano())

    start := time.Now()
    results := google("golang")
    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 google(query string) (results []result) {
    c := make(chan result)

    go func() {
        c <- web(query)
    }()

    go func() {
        c <- image(query)
    }()

    go func() {
        c <- video(query)
    }()

    for i := 0; i < 3; i++ {
        r := <-c
        results = append(results, r)
    }

    return results
}

示例7:google search 2.1

// googlesearch/googlesearch2.1
/*
Example: Google Search 2.1
Given a query, return a page of search results (and some ads).
Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results.
Don't wait for slow servers. No locks. No condition variables. No callbacks
Run each search in their own Goroutine but only return any searches that complete in
80 Milliseconds or less
*/
package main

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

var (
    web   = fakeSearch("web")
    image = fakeSearch("image")
    video = fakeSearch("video")
)

type (
    result string
    search func(query string) result
)

func main() {
    rand.Seed(time.Now().UnixNano())

    start := time.Now()
    results := google("golang")
    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 google(query string) (results []result) {
    c := make(chan result)

    go func() {
        c <- web(query)
    }()

    go func() {
        c <- image(query)
    }()

    go func() {
        c <- video(query)
    }()
    timeout := time.After(80 * time.Millisecond)

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

    return results
}

示例8:google search 3.0

// googlesearch/googlesearch3.0
/*
Example: Google Search 3.0
Given a query, return a page of search results (and some ads).
Send the query to web search, image search, YouTube, Maps, News, etc. then mix the results.
No locks. No condition variables. No callbacks
Reduce tail latency using replicated search servers
Run the same search against multiple servers in their own Goroutine but only return searches
that complete in 80 Milliseconds or less
All three searches SHOULD always come back in under 80 milliseconds
*/
package main

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

var (
    web1   = fakeSearch("web")
    web2   = fakeSearch("web")
    image1 = fakeSearch("image")
    image2 = fakeSearch("image")
    video1 = fakeSearch("video")
    video2 = fakeSearch("video")
)

type (
    result string
    search func(query string) result
)

func main() {
    rand.Seed(time.Now().UnixNano())

    start := time.Now()
    results := google("golang")
    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 google(query string) (results []result) {
    c := make(chan result)

    go func() {
        c <- first(query, web1, web2)
    }()

    go func() {
        c <- first(query, image1, image2)
    }()

    go func() {
        c <- first(query, video1, video2)
    }()
    timeout := time.After(80 * time.Millisecond)

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

    return results
}

func first(query string, replicas ...search) result {
    c := make(chan result)

    // Define a function that takes the index to the replica function to use.
    // Then it executes that function writing the results to the channel.
    searchReplica := func(i int) {
        c <- replicas[i](query)
    }

    // Run each replica function in its own Goroutine.
    for i := range replicas {
        go searchReplica(i)
    }

    // As soon as one of the replica functions write a result, return.
    return <-c
}

参考链接

おすすめ

転載: www.cnblogs.com/huahuayu/p/12235248.html