• Go语言学习笔记(4)并发


    Go 例程(Goroutines)

    Go 例程是一种绿色线程,使用关键字 go 来启动。

    https://gobyexample.com/goroutines

    func say(s string) {
        for i := 0; i < 5; i++ {
            time.Sleep(100 * time.Millisecond)
            fmt.Println(s)
        }
    }
    go say("world")
    say("hello")
    /*
    world
    hello
    hello
    world
    hello
    world
    hello
    world
    hello
    */
    

    信道(Channels)

    // 无缓冲的信道的创建,发送和接收
    ch := make(chan int)
    ch <- v    // Send v to channel ch.
    v := <-ch  // Receive from ch, and
               // assign value to v.
    
    // Go 例程 + 信道
    func sum(s []int, c chan int) {
        sum := 0
        for _, v := range s {
            sum += v
        }
        c <- sum // send sum to c
    }
    s := []int{7, 2, 8, -9, 4, 0}
    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c // receive from c
    fmt.Println(x, y, x+y) // -5 17 12
    
    // 有缓冲的信道的创建,发送和接收
    ch := make(chan int, 2)
    ch <- 1 // send
    ch <- 2 // send
    // ch <- 3 // send // error
    fmt.Println(<-ch) // receive
    fmt.Println(<-ch) // receive
    // ch <- 3 // send // ok
    
    // 单向信道
    // Only for receiving
    mychanl1 := make(<-chan string)
    // Only for sending
    mychanl2 := make(chan<- string)
    // 单向信道通常用作Go例程形参类型
    func pong(pings <-chan string, pongs chan<- string) {
        msg := <-pings
        pongs <- msg
    }
    
    • 信道是(Go例程之间)用来通信的管道
    • 信道是类型安全的
    • 信道的数据结构是先进先出的队列
    • 无缓冲的信道是同步的,发送和接收必须同时进行,不然会导致堵塞
    • 有缓冲的信道是异步的,发送和接收不必同时进行
    • 单向信道可以进一步增强安全性

    遍历信道,关闭信道

    // 调用 close 函数关闭信道
    // 使用 for 循环遍历信道
    func fibonacci(n int, c chan int) {
        x, y := 0, 1
        for i := 0; i < n; i++ {
            c <- x // send
            x, y = y, x+y
        }
        close(c)
    }
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    for i := range c { // receive
        fmt.Println(i)
    }
    /*
    0
    1
    1
    2
    3
    5
    8
    13
    21
    34
    */
    

    选择信道

    使用 select 语句可以同时等待多个信道的发送和接收操作。

    // 使用 select 语句实现线程同步
    func fibonacci(c, quit chan int) {
        x, y := 0, 1
        for {
            select {
            case c <- x: // send
                x, y = y, x+y
            case <-quit: // receive
                fmt.Println("quit")
                return
            }
        }
    }
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c) // receive
        }
        quit <- 0 // send
    }()
    fibonacci(c, quit)
    /*
    0
    1
    1
    2
    3
    5
    8
    13
    21
    34
    quit
    */
    
    // 使用 select 语句实现 Timeout
    tick := time.Tick(100 * time.Millisecond) // send
    boom := time.After(500 * time.Millisecond) // send
    for {
        select {
        case <-tick: // receive
            fmt.Println("tick.")
        case <-boom: // receive
            fmt.Println("BOOM!")
            return
        default:
            fmt.Println("    .")
            time.Sleep(50 * time.Millisecond)
        }
    }
    /*
        .
        .
    tick.
        .
        .
    tick.
        .
        .
    tick.
        .
        .
    tick.
        .
        .
    BOOM!
    */
    

    sync.Mutex

    排它锁

    // SafeCounter is safe to use concurrently.
    type SafeCounter struct {
        v   map[string]int
        mux sync.Mutex
    }
    // Inc increments the counter for the given key.
    func (c *SafeCounter) Inc(key string) {
        c.mux.Lock()
        // Lock so only one goroutine at a time can access the map c.v.
        c.v[key]++
        c.mux.Unlock()
    }
    // Value returns the current value of the counter for the given key.
    func (c *SafeCounter) Value(key string) int {
        c.mux.Lock()
        // Lock so only one goroutine at a time can access the map c.v.
        defer c.mux.Unlock()
        return c.v[key]
    }
    c := SafeCounter{v: make(map[string]int)}
    for i := 0; i < 1000; i++ {
        go c.Inc("somekey")
    }
    time.Sleep(time.Second)
    fmt.Println(c.Value("somekey")) // 1000
    

    sync.WaitGroup

    线程间同步

    func process(i int, wg *sync.WaitGroup) {  
        fmt.Println("started Goroutine ", i)
        time.Sleep(2 * time.Second)
        fmt.Printf("Goroutine %d ended
    ", i)
        wg.Done()
    }
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go process(i, &wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
    /*
    started Goroutine  2
    started Goroutine  0
    started Goroutine  1
    Goroutine 2 ended
    Goroutine 1 ended
    Goroutine 0 ended
    All go routines finished executing
    */
    

    参考链接

    Is a Go goroutine a coroutine?

  • 相关阅读:
    个人学期总结
    实验四:201571030116/201571030106《小学四则运算练习软件需求说明》结对项目报告
    201571030106/201571030116《小学四则运算练习软件》结对项目报告
    java实现随机四则运算
    《构建之法》不得不提的五个问题
    项目自动化建构工具gradle 入门5——在intellij中做一个gradle的web工程
    项目自动化建构工具gradle 入门4——javaWeb在浏览器中显示helloWorld
    项目自动化建构工具gradle 入门3——生一个exe的helloWorld
    项目自动化建构工具gradle 入门0——环境 & 废话
    项目自动化建构工具gradle 入门2——log4j输出helloWorld
  • 原文地址:https://www.cnblogs.com/zwvista/p/12888115.html
Copyright © 2020-2023  润新知