go并发: 并发时不用channel行不行?

一,并发时不使用channel,直接访问变量(共享内存)

代码:

// 入口函数
func main() {
    //共享访问的切片
    var ints []int
    var wg sync.WaitGroup
    //定义协程的数量
    n:=10000
    //定义wg的数量
    wg.Add(n)
    //创建指定数量的协程
    for i := 0; i < n; i++ {
        go func(i int) {
            //fmt.Println("协程中写入:",i)
            defer wg.Done()
            ints = append(ints, i) // 多 goroutine 访问共享内存
        }(i)
    }
    //等待结束
    wg.Wait()
    //打印数量
    fmt.Println(len(ints))
}

运行效果:

$ go run main.go 
9123
$ go run main.go 
9835
$ go run main.go 
10000
$ go run main.go 
9976
$ go run main.go 
9999
$ go run main.go 
9979

虽然有可以正确写入的时候,但很多时候发生了没能成功写入的情况,
这就是并发访问共享内存,而没有相应的并发处理机制导致的后果

二,改进:用互斥锁 (Mutex) 保证对共享变量的安全访问

代码:

// 入口函数
func main() {
    //共享访问的切片
    var ints []int
    var wg sync.WaitGroup
    var mux sync.Mutex
    //定义协程的数量
    n:=10000
    //定义wg的数量
    wg.Add(n)
    //创建指定数量的协程
    for i := 0; i < n; i++ {
        go func(i int) {
            //fmt.Println("协程中写入:",i)
            defer wg.Done()
            mux.Lock()     //加锁
            ints = append(ints, i) // 多 goroutine 访问共享内存
            mux.Unlock()   //解锁
        }(i)
    }
    //等待结束
    wg.Wait()
    //打印数量
    fmt.Println(len(ints))
}

运行效果:

$ go run main.go 
10000
$ go run main.go 
10000
$ go run main.go 
10000
$ go run main.go 
10000
$ go run main.go 
10000
$ go run main.go 
10000

可见:在访问共享内存之前:先获取锁,

访问之后再解锁,这样不会再出现因并发导致的写入失败的情况

三,改进:并发写入时用channel实现,之后再从channel中读到到变量中

代码:

// 入口函数
func main() {
    //共享访问的切片
    var ints []int
    //定义协程的数量
    n:=10000
    // 创建缓冲区为 n 的 Channel
    channel := make(chan int, n)
    //写入数据到channel
    for i := 0; i < n; i++ {
        go func(ch chan<- int, val int) {
            ch <- val // 写入 Channel
        }(channel, i)
    }
    //从channel中读取数据
    for i := range channel {
        ints = append(ints, i)
        if len(ints) == n {
            break
        }
    }
    // 关闭 Channel
    close(channel)

    //打印数量
    fmt.Println(len(ints))
}

运行效果:

$ go run main.go 
10000
$ go run main.go 
10000
$ go run main.go 
10000
$ go run main.go 
10000
$ go run main.go 
10000
$ go run main.go 
10000

 

posted @   刘宏缔的架构森林  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
历史上的今天:
2021-03-01 spring boot单元测试之二:用MockMvc为controller做单元测试(spring boot 2.4.3)
2020-03-01 docker的常用操作之二:docker内无法解析dns之firewalld设置等
2020-03-01 centos8安装fastdfs6.06集群方式三之:storage的安装/配置/运行
2020-03-01 centos8安装fastdfs6.06集群方式二之:tracker的安装/配置/运行
2020-03-01 centos8安装fastdfs6.06集群方式一之:软件下载与安装
2020-03-01 fastdfs之同一台storage server下包含多个store path
2020-03-01 centos8安装fastdfs6.06(单机方式)
点击右上角即可分享
微信分享提示