ch14_并发concurrency

并发concurrency

简单示例

  • 任何一个函数在调用前加go便可以实现并发
  • 注意如果不加任何通信,由于go所执行的协程并不会阻塞主函数的执行,主函数会同时继续向后执行,直到结束时退出。
func Hello(name string) {
    fmt.Println("Hello", name)
}

func main() {
    //一般情况下这段代码不会有任何输出,因为在调用输出函数之前main函数已经结束了
    go Hello("Bob")
}

Channel

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

创建channel

    c := make(chan int)

使用channel

    c <- 1 //向c中写入一个数字1
    tmp := <-c //从c中读出一个数字并存到一个新变量tmp中
  • channel是引用类型,传递参数传的是引用
  • 读/写 无缓存 channel会造成阻塞

关闭channel

    close(c)

示例改进

func Hello(c chan bool, name string) {
    fmt.Println("Hello", name)
    c <- true
}

func main() {
    c := make(chan bool)
    go Hello(c, "Bob")
    <-c
}

遍历channel

  • 遍历channel一定要注意能正确关闭channel,否则可能会发生死锁
    for v := range c {
        fmt.Println(v)
    }

有缓存channel

  • 区别
    • 无缓存channel读写之间是同步的,是相互阻塞的,写入channel数据的操作会等待取出channel数据运行完之后才结束
    • 有缓存channel读写是异步的,即写入操作只要成功写入后就结束,不管读取操作成功执行与否
    c := make(chan int) //无缓存channel
    d := make(chan int, 1) //有缓存channel,缓存容量为1

以下代码运行时将没有输出

func Hello(c chan bool, name string) {
    fmt.Println("Hello", name)
    <-c
}
func main() {
    //此处如果去掉1或者把1改为0,就会变成同步操作
    c := make(chan bool, 1)
    go Hello(c, "Bob")
    //异步通信,数据写入完就结束,不管其他协程是否取出数据
    c <- true
}

一个多协程通信的例子

func Counter(c chan bool, index int) {
    a := 1
    for i := 0; i < 100000000; i++ {
        a += i
    }
    fmt.Println(index, a)
    c <- true
}
func main() {
    t1 := time.Now()
    //设置使用的CPU核心数
    //runtime.GOMAXPROCS(runtime.NumCPU())
    //c := make(chan bool, 10) //有缓存
    c := make(chan bool) //无缓存
    for i := 0; i < 10; i++ {
        go Counter(c, i)
    }
    for i := 0; i < 10; i++ {
        <-c
    }
    t2 := time.Now()
    fmt.Println(t2.Sub(t1))
}
  • 虽然一些教材上说goruntime默认运行在一个CPU核心上,但是考虑到教材内容无法与时俱进,经过自己尝试后发现是否添加runtime.GOMAXPROCS(runtime.NumCPU())程序运行时间差异不大,而将参数设为1后会明显变慢,故猜测当前版本默认肯定不是单核心。具体解释需要在官方文档找到答案才好(原谅本菜鸟不知怎么找官方解释……)

Select

  • 类似于switch语句,但case都是channel的相关操作,用来监听和channel有关的IO操作,当 IO 操作发生时,触发相应的动作。
  • 可处理一个或多个 channel 的发送与接收
  • 同时有多个可用的 channel时按随机顺序处理
  • 可用空的 select 来阻塞 main 函数
  • 可设置超时
func main() {
    c1, c2 := make(chan int), make(chan string)
    o := make(chan bool)
    go func() {
        for {
            select {
            case v, ok := <-c1:
                if ok == false {
                    o <- true
                    break
                }
                fmt.Println("c1", v)
            case v, ok := <-c2:
                if ok == false {
                    o <- true
                    break
                }
                fmt.Println("c2", v)
            }
        }
    }()
    c1 <- 1
    c2 <- "Hello"
    c1 <- 2
    c2 <- "Test"
    close(c1)
    close(c2)
    <-o
}
//一个用select随机生成01串的例子
func main() {
    c := make(chan int)
    go func() {
        for v := range c {
            fmt.Print(v)
        }
    }()
    for i := 0; i < 10; i++ {
        select {
        case c <- 0:
        case c <- 1:
        }
    }
    close(c)
}
//在select中判断超时
func main() {
    c := make(chan int)
    select {
    case v := <-c:
        fmt.Println(v)
    case <-time.After(3 * time.Second):
        fmt.Println("Timeout")
    }
    close(c)
}

同步

*需要导入sync包

sync.WaitGroup

  • 可以用其创建任务组
    wg := sync.WaitGroup{} //创建一个任务组
    wg.add(10) //增加10个任务
    wg.Done()  //完成一个任务
    wg.Wait()  //等待任务执行结束
  • 由于是值拷贝,所以需要传入指针

一个多协程同步的例子

func Counter(wg *sync.WaitGroup, index int) {
    a := 1
    for i := 0; i < 100000000; i++ {
        a += i
    }
    fmt.Println(index, a)
    wg.Done()
}
func main() {
    t1 := time.Now()
    //runtime.GOMAXPROCS(runtime.NumCPU())
    wg := sync.WaitGroup{}
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go Counter(&wg, i)
    }
    wg.Wait()
    t2 := time.Now()
    fmt.Println(t2.Sub(t1))
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值