• 6.并发


    1. goroutine

    go关键字实现的最简单的并发, 注意程序会在main函数结束时退出程序,并不会等待其他goroutin结束。

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func Add(x, y int)  {
        z := x + y
        fmt.Println(z)
    }
    
    func main() {
        x := 1
        y := 1
        for i:=0; i<10 ;i++  {
            go Add(x, y)
        }
    
        time.Sleep(4)
    }

    2.并发通信

    并发通信通过 共享数据 和 消息来实现

    package main
    import (
        "fmt"
        "runtime"
        "sync"
    )
    var counter = 0
    func Add(lock *sync.Mutex)  {
        lock.Lock()
        counter += 1
        lock.Unlock()
    }
    func main() {
        lock := &sync.Mutex{}
        for i:=0 ; i<10 ; i++  {
            go Add(lock)
        }
        for {
            lock.Lock()
            c := counter
            lock.Unlock()
            runtime.Gosched()   // 让出cpu的时间片,程序遇到goshed就好让出cpu让给其他goroutine执行
            if c >= 10{
                break
            }
        }
        fmt.Println(counter)
    }

    3. chanel

    package main
    
    import "fmt"
    
    func Count(count *int, ch chan int)  {
        *count = *count + 1
        fmt.Println(*count)
        ch <- 1    // 想channel 中写入数据,在channel中的数据被读取之前,这个操作是阻塞的
    }
    
    func main() {
        count := 0
        chs := make([]chan int, 5)
        for i:=0;i<5;i++{
            chs[i] = make(chan int)
            go Count(&count,chs[i])
        }
        for _, ch := range chs{
            <- ch   // 从channel中读取数据,在数据被写入之前,这个操作是
        }
    }

    3.1 基本语法

    // 声明
    var chanName chan ElementType
    //在类型之前加了一个chan关键字
    
    var ch chan int
    
    var m map[string] chan bool
    
    // 定义
    ch = make(chan int)   // 初始化一个int型的channel
    
    
    //channel 的读写
    ch <- value // 将一个数据发送至channel, 写入数据会产生阻塞,知道其他grountine从channel中将数据读走
    varlue = <-ch  // 从channel读取数据,如果channel中没数据也会导致阻塞,知道有数据写入
    package main
    
    import "fmt"
    
    func Counsumer(ch1, ch2 chan int)  {
        var value int
        for {
            value = <-ch1
            if value >= 100{
                ch2 <- 0
            }
            fmt.Println("消费了", value)
        }
    }
    
    func main() {
        ch1 := make(chan int)
        ch2 := make(chan int)
        go Counsumer(ch1, ch2)
        var i int = 0
        for i < 100{
            i += 1
            ch1 <- i
            fmt.Println("生产了", i)
        }
        <- ch2
    }

    3.2 select

    select 语法与switch相似,只不过每一个case条件必须是一个channel操作。

    package main
    import "fmt"
    func main() {
        ch := make(chan int, 1)
        j := 0
        for j < 100{
            j += 1
            select {
            case ch <- 0:
            case ch <- 1:
            }
            i := <-ch
            fmt.Println(i)
        }
    }

    执行上面代码会发现打印的数字有时为1有时为0。这与select的机制有关,它是随机的。

    3.3 缓冲机制

    package main
    
    import "fmt"
    
    func Consumer(ch chan int)  {
        for{
            value := <-ch
            fmt.Println("消费了", value)
        }
    }
    
    func main() {
        ch := make(chan int, 100)   // 构造channel的时候可以指定channel的缓存大小
        i := 0                                 // 设置缓存大小后,如果chan没满则生产环节不再阻塞
    
        go Consumer(ch)
    
        for i < 100{
            i += 1
            print("生产", i, "
    ")
            ch <- i
        }
    }

    带缓冲区的channel还可以这样读

    package main
    
    import (
        "fmt"
    )
    
    func Consumer(ch, ch2 chan int)  {
        Loop1:
        for   {
            //value := <-ch
            for value := range ch{
                fmt.Println("消费了", value)
                if value >= 100{
                    break Loop1
                }
            }
        }
        fmt.Println("lalala")
        ch2 <- 0
    }
    
    func main() {
        ch := make(chan int, 100)
        ch2 := make(chan int)
        i := 0
        go Consumer(ch, ch2)
        for i < 100{
            i += 1
            print("生产", i)
            ch <- i
        }
        <-ch2
    }

    3.4超时机制

    package main
    
    import (
        "fmt"
        "time"
    )
    
    
    func main() {
        timeout := make(chan int, 1)
        func() {
            time.Sleep(1)
            timeout <- 1
        }()
        ch := make(chan int, 1)
        ch <- 1
        t := time.Now()
        select{
        case <-ch:
            fmt.Println("从ch中读到数据")
        case <-timeout:
            fmt.Println("从timeout读取数据")
        }
        fmt.Println(time.Now().Sub(t))
    }

     3.5 channel的传递                                                                                                                                                             

    chanel本身也是原生类型,与map之类的类型一样,channel定义之后也可以通过channel传递

    package main
    
    type Pipe struct {
        value int
        handler func(i int) int
        next chan int
    }
    
    func handle(queue chan *Pipe)  {
        for data := range queue{
            data.next <- data.handler(data.value)
        }    
    }
    
    func add(i int)int  {
        return i + 1
    }
    
    func main() {
        var p1 Pipe = Pipe{1, add, make(chan int,1)}
    }

    3.6 单向channel

    var ch1 chan int   // 正常的channel
    var ch2 chan<- float64  // ch2是单向channel, 只能用于写float64数据
    var ch3 <-chan int        // 只能用于读取int数据
    
    
    ch4 := make(chan int)
    ch5 := <-chan int(ch4)    // 将ch4转换为单向读取channel
    ch6 := chan<- int(ch4)    // 将ch4转换为单向写channel
    
    
    // 用法
    func Parse(ch <-chan int) {
        for value := range ch{    // 在这个函数中只能对ch进行读操作
            fmt.Println(value)
        }
    }
    package main
    
    import (
        "fmt"
        "time"
    )
    
    func read(ch <-chan int)  {
        value := <-ch
        fmt.Println(value)
    }
    
    func main() {
        ch4 := make(chan int, 1)
        go read(ch4)
        ch4 <- 1
        time.Sleep(1e9)
    }

    3.7 关闭channel

    close(ch)

    3.8 出让时间片

    runtime.Gosched()
    // 如果要精细的控制goroutine的行为,就必须深入的了解runtime包提供的具体功能

    3.9 同步

    3.9.1同步锁:

      sync.Mutex 

      sync.RWmutex  单写多读模型,一个goroutine获取读锁之后,只限制写,其他goroutine仍可以读。而写锁会组织所有goroutine获取读锁和写锁

      Lock 写锁

      RLock 读锁

    var l sync.Mutex
    func foo() {
        l.Lock()
        defer l.Unlock()  
    }

    3.9.2全局唯一性操作

    先来看一段代码

    package main
    
    import "fmt"
    
    var done bool = false
    
    func setup()  {
        fmt.Println("hello, world")
        done = true
    }
    
    func doprint() {
        if !done{
            setup()
        }
    }

    这段代码的目的是保证setup函数只被执行一次。但是细看还是会有问题,因为setup并不是一个原子性操作, 这种写法可能会导致setup函数被多次调用。

        

  • 相关阅读:
    Gamma阶段第三次scrum meeting
    【技术博客】Django+uginx+uwsgi框架的服务器部署
    Gamma阶段第二次scrum meeting
    Gamma阶段第一次scrum meeting
    团队项目贡献分
    Beta阶段发布说明
    Beta阶段测试报告
    【Beta阶段】第十次Scrum Meeting
    团队贡献分汇总
    [Gamma]Scrum Meeting#4
  • 原文地址:https://www.cnblogs.com/zhangjian0092/p/12370565.html
Copyright © 2020-2023  润新知