GO入门——7. 并发

1 并发

1.1 goroutine

Goroutine 奉行通过通信来共享内存,而不是共享内存来通信

  • goroutine 只是由官方实现的超级“线程池”而已。
  • 每个实例 4-5KB 的栈内存占用和由于实现机制而大幅

    减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。

  • goroutine 的简单易用,也在语言层面上给予了开发者巨大的便利

1.2 Channel

  • Channel 是 goroutine -沟通的桥梁,大都是阻塞同步的
  • 通过 make 创建,close 关闭
  • Channel 是引用类型
  • 可以使用 for range 来迭代不断操作 channel
  • 可以设置单向或双向通道
  • 可以设置缓存大小,在未被填满前不会发生阻塞

1.3 Select

  • 可处理一个或多个 channel 的发送与接收
  • 同时有多个可用的 channel时按随机顺序处理
  • 可用空的 select 来阻塞 main 函数
  • 可设置超时
package main

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

func main() {
        testSelectTimeout()
}

func testMain() {
        //当只执行这一句时没有结果输出
        //原因在于go将函数放入其他线程执行,当main结束后其他线程也将结束
        go func() {
                fmt.Println("Go GO GO !!!")
        }()
}

//channel是引用类型,所以不需要传指针类型即可修改
func goFunc(c chan bool) {
        fmt.Println("GO GO GO")
        c <- true
}

//channel
func testChannel() {
        //make(chan bool,x),设置缓存为x
        //当有缓存时,如果缓存还没满则向channel插入值是不会阻塞的
        //创建无缓存的channel
        c := make(chan bool)
        go goFunc(c)
        //此时从channel中取值,如果channel中没有值则将阻塞
        //因此必然将先执行完goFunc然后main才结束
        <-c
}

//channel配合range
func testChannelRange() {
        c := make(chan bool)
        go func() {
                fmt.Println("GO GO GO")
                c <- true
                close(c) //关闭channel,如果不关闭range将一直阻塞main线程
        }()
        for v := range c { //从channel中一直取值,直到channel被关闭
                fmt.Println(v) //输出true
        }
}

//
func testCurrentByChannel() {
        //设置GO使用cup核数,好像默认就是多核
        runtime.GOMAXPROCS(runtime.NumCPU())
        c := make(chan bool, 10)
        f := func(index int) {
                sum := 0
                for i := 0; i < 1000000; i++ {
                        sum += i
                }
                fmt.Println(index, sum)
                c <- true
        }
        for i := 0; i < 10; i++ {
                go f(i)
        }
        for i := 0; i < 10; i++ {
                <-c
        }
}

//WaitGroup类似java中的CountDownLatch
func testCurrentByWatiGroup() {
        //设置GO使用cup核数,好像默认就是多核
        runtime.GOMAXPROCS(runtime.NumCPU())
        wg := sync.WaitGroup{}
        wg.Add(10)
        //需要传入指针
        f := func(index int, wg *sync.WaitGroup) {
                sum := 0
                for i := 0; i < 1000000; i++ {
                        sum += i
                }
                fmt.Println(index, sum)
                wg.Done()
        }
        for i := 0; i < 10; i++ {
                go f(i, &wg)
        }
        wg.Wait()
}

//有问题。。。。
//当channel都无缓存时关闭其中一个channel,再向另一个写值,此时将发生死锁
//当channel有缓存,关闭的那个channel将一直输出(空,false)
func testSelect() {
        //select 无法保证多个channel都关闭
        c1, c2 := make(chan int), make(chan bool)
        o := make(chan bool)
        go func() {
                //f1, f2 := false, false
                for {
                        select {
                        //当某个channel关闭后,其case将一直执行,返回一个(v=空值,ok=false)
                        //因此此时select将
                        case v, ok := <-c1:
                                {
                                        fmt.Println("oc1")
                                        if !ok {
                                                fmt.Println("c1")
                                                o <- true
                                                break
                                        }
                                        fmt.Println("c1", v)
                                }
                        case v, ok := <-c2:
                                {
                                        if !ok {
                                                o <- true
                                                break
                                        }
                                        fmt.Println("c2", v)
                                }
                        }
                }
        }()
        c1 <- 0
        c1 <- 1
        c2 <- true
        c1 <- 2
        c2 <- false
        c1 <- 3

        close(c1)
        //close(c2)
        c2 <- false
        //c2 <- false
        for i := 0; i < 2; i++ {
                <-o
                fmt.Println("o")
        }

}

//select作为发送者
func testSelectSend() {
        c := make(chan int)

        go func() {
                for i := 0; i < 10; i++ {
                        //随机向c中写入0或1
                        select {
                        case c <- 0:
                        case c <- 1:
                        }
                }
        }()
        for i := 0; i < 10; i++ {
                fmt.Println(<-c)
        }
        select {} //阻塞程序
}

//select timeout
func testSelectTimeout() {
        c := make(chan int)
        go func() {
                for i := 0; i < 5; i++ {
                        time.Sleep(1 * time.Second)
                        c <- i
                }

        }()
Label:
        for {//不加for则只会输出一次case的匹配就结束了
                select {
                case v := <-c:
                        fmt.Println(v)
                case <-time.After(3 * time.Second):  //3s没获取到值时将执行
                        fmt.Println("Timeout") //输出
                        break Label     //退出select死循环
                }
        }
}