前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >(四十二)golang--协程之间通信的方式

(四十二)golang--协程之间通信的方式

作者头像
西西嘛呦
发布2020-08-26 14:32:42
1.1K0
发布2020-08-26 14:32:42
举报
文章被收录于专栏:数据分析与挖掘

假设我们现在有这么一个需求:

计算1-200之间各个数的阶乘,并将每个结果保存在mao中,最终显示出来,要求使用goroutime。

分析:

(1)使用goroutime完成,效率高,但是会出现并发/并行安全问题;

(2)不同协程之间如何通信;

  • 对于(1):不同协程之间可能同时对一块内存进行操作,导致数据的混乱,即并发/并行不安全;主协程运行完了,计算阶乘的协程却没有运行完,功能并不能够准确实现;可利用互斥锁解决该问题;
  • 对于(2):可以利用利用管道;

正常的代码:

代码语言:javascript
复制
package main

import (
    "fmt"
    "sync"
)

var (
    myMap = make(map[int]int, 10)
)

func cal(n int) {
    res := 1
    for i := 1; i <= n; i++ {
        res *= i
    }
    myMap[n] = res
}

func main() {
    for i := 1; i <= 15; i++ {
        go cal(i)
    }
    for i, v := range myMap {
        fmt.Printf("map[%d]=%d\n", i, v)
    }
}

运行结果:

1.利用互斥锁 

代码语言:javascript
复制
package main

import (
    "fmt"
    "sync"
  ""  

)

var (
    myMap = make(map[int]int, 10)
    //lock是全局互斥锁,synchornized
    lock sync.Mutex
)

func cal(n int) {
    res := 1
    for i := 1; i <= n; i++ {
        res *= i
    }
    lock.Lock()
    myMap[n] = res
    lock.Unlock()
}

func main() {
    for i := 1; i <= 15; i++ {
        go cal(i)
    }

    for i, v := range myMap {
        fmt.Printf("map[%d]=%d\n", i, v)
    }
}

有可能主程序运行完了而cal还没运行完(上面结果只到13,没有14,15),需要加上time.Sleep(time.Seconde*3),而在输出时,由于主协程并不知道程序已经完成了,底层仍然可能出现竞争资源,所以在输出阶段也要加上互斥锁。最终代码如下:

代码语言:javascript
复制
package main

import (
    "fmt"
    "sync"
)

var (
    myMap = make(map[int]int, 10)
    //lock是全局互斥锁,synchornized
    lock sync.Mutex
)

func cal(n int) {
    res := 1
    for i := 1; i <= n; i++ {
        res *= i
    }
    lock.Lock()
    myMap[n] = res
    lock.Unlock()
}

func main() {
    for i := 1; i <= 15; i++ {
        go cal(i)
    }

    time.Sleep(time.Second * 4)

    lock.Lock()
    for i, v := range myMap {
        fmt.Printf("map[%d]=%d\n", i, v)
    }
    lock.Unlock()
}

为什么需要管道?

(1)主线程在等待所有协程全部完成的时间很难确定;

(2)如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能协程还处于工作状态,这时也会随着主协程的结束而销毁;

(3)通过全局变量加锁同步来实现通讯,也并不利于多个协程对全局变量的读写操作;

管道的介绍: (1)管道的本质就是一种数据结构--队列;

(2)数据先进先出;

(3)线程安全,多协程访问时,不需要加锁;

(4)管道只能存储相同的数据类型;

管道的声明:

var intChan chan int;

var stringChan chan string;

var mapChan chan map[int]string;

var perChan chan Person;

var perChan chan *Person;

注意:管道是引用类型;管道必须初始化后才能写入数据;管道是有类型的,即IntChan只能写入int;

管道初始化:

var intChan chan int

intChan = make(chan int,10) 

向管道中读写数据:

num := 10

intChan<-num

var num2 int

num2<-intChan

注意:管道容量满了则不能继续写入,在没有使用协程的情况下,管道空了不能继续读取。

如何使管道中存储任意数据类型?

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-11-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档