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
浙公网安备 33010602011771号