本文共 1931 字,大约阅读时间需要 6 分钟。
保证操作安全的方法:
特定约束:即通过约定来实现的,在大型项目中很难保证。例子:
data := make([]int, 4)loopData := func( handleData chan<- int){ defer close(handleData) for i := range data { handleData <- data[i] }}handleData := make(chan int)go loopData(handleData)for num := range handleData { fmt.Println(num)}https://play.golang.org/p/f-EpjaA2k6G在loopData函数和handleData channel里的循环都可以使用data切片。但是我们希望的是在loopData中使用,因此随着时间和更多的人使用代码,约束也会被打破。
词法约束: 涉及使用词法作用域仅公开用于多个并发进程的正确数据和并发原语;例子:
1、channel相关func main() { chanOwner := func() <-chan int { results := make(chan int, 5) // 1 go func() { defer close(results) for i := 0; i <= 5; i++ { results <- i } }() return results } consumer := func(results <-chan int) { // 3 for result := range results { fmt.Printf("Received: %d\n", result) } fmt.Println("Done") } results := chanOwner() // 2 consumer(results)}https://play.golang.org/p/qUSo5ISc0gz1、例子中chanOwer函数在词法范围内实例化channel,防止channel被其他goroutine改写2、收到channel的读处理,并将其传递给消费者3、收到channel的只读副本,consumer的作用就是对channel的只读处理这个就是和对结构体字段的get和set的道理是一样的,对读写进行分离
2、普通的并发不安全的
printData := func(wg *sync.WaitGroup, data []byte) { defer wg.Done() var buff bytes.Buffer for _, b := range data { fmt.Fprintf(&buff, "%c", b) } fmt.Println(buff.String()) } var wg sync.WaitGroup wg.Add(2) data := []byte("golang") go printData(&wg, data[:3]) go printData(&wg, data[3:]) wg.Wait() 可以看到printData不能直接访问data的数据,必须要占用data的一部分或者全部才能对data进行操作 这样就约束了goroutine只能去操作我们传进去的数据而不是其他数据,在这里我们可以不通过通信来完成内存访问同步或共享数据。
通过前面的约束可以看到虽然建立约束是困难的,但是对于性能等的提高是明显的。
一般模式
for { select { // 使用channel进行操作 }}变体1、for { select { case <- done: return default: } // 进行非抢占式任务}变体2for { select { case <- done: return default: // 进行非抢占式任务 }}
for-select这种模式经常会被用到,虽然看起来很简单。
转载地址:http://wcfkb.baihongyu.com/