예를 병행 프로그래밍을 이동

내 독립 블로그에서 재판 : 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)
}

goroutine

그냥 goroutine을 시작하는 키워드 이동을 사용, 그것은 비동기 비 차단 방식을 사용하여 실행됩니다.

// 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 소비

// 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