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 @ 2025-03-01 20:54  刘宏缔的架构森林  阅读(31)  评论(0)    收藏  举报