• GO并发安全


    资源竞争


    • 多协程并发修改同一块内存,产生资源竞争

    • go run或go build时添加-race参数检查资源竞争情况

    • n++不是原子操作,并发执行时会存在脏写。n++分为3步:取出n,加1,结果赋给n

      脏写现象

      package main
      
      import (
      	"fmt"
      	"sync"
      	"sync/atomic"
      )
      
      var n int32
      
      func inc() {
      	n++
      }
      
      func main() {
      	const P = 100
      	wg := sync.WaitGroup{}
      	wg.Add(P)
      	for i := 0; i < P; i++ {
      		go func() {
      			defer wg.Done()
      			inc()
      		}()
      	}
      	wg.Wait()
      	fmt.Printf("n=%d\n", n)
      }
      
      // go run .\atomic_channel.go 概率性出现
      n=99
      // go run .\atomic_channel.go
      n=100
      

    原子操作


    使用atomic封装原子操作,解除资源竞争,避免脏写

    • atomic.LoadInt32
    • atomic.AddInt32
    package main
    
    import (
    	"fmt"
    	"sync"
    	"sync/atomic"
    )
    
    var n int32
    
    func inc() {
    	n++
    }
    
    func inc2() {
    	atomic.AddInt32(&n, 1)
    }
    
    func main() {
    	const P = 100
    	wg := sync.WaitGroup{}
    	wg.Add(P)
    	for i := 0; i < P; i++ {
    		go func() {
    			defer wg.Done()
    			inc2()
    		}()
    	}
    	wg.Wait()
    	fmt.Printf("n=%d\n", n)
    	// fmt.Printf("n=%d\n", atomic.LoadInt32(&n))
    }
    
    // go run .\atomic_channel.go
    n=100
    

    读写锁


    • 读写锁包含sync.Mutex,一般用读写锁
    • var lock2 sync.RWMutex 声明读写锁,无需初始化
    • lock.Lock() lock.Unlock 加写锁和释放写锁
    • lock.RLock() lock.RUnlock() 加读锁和释放读锁
    • 任意时刻只可以加一把写锁,且不能再加读锁
    • 没加写锁时,可以同时加多把读锁

    示例

    var lock sync.RWMutex
    
    func main() {
    	lock.Lock // 写锁
    	n++       // 临界区-锁和释放锁之间的代码
    	lock.Unlock // 释放写n锁
    
    	lock.RLock //读锁
      fmt.Printf("n=%d\n", n)
    	lock.RUnlock //释放读锁
    }
    

    容器的并发安全性


    • 数组、slice、struct允许并发修改(可能会脏写),并发修改map会发生panic
    • 如果需要并发修改map请使用sync.Map

    并发修改数组

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    var arr = [10]int{}
    
    func main() {
    	wg := sync.WaitGroup{}
    	wg.Add(2)
    	go func() {
    		defer wg.Done()
    		for i := 1; i < 10; i += 2 {
    			arr[i] = 3
    		}
    	}()
    	go func() {
    		defer wg.Done()
    		for i := 0; i < 10; i += 2 {
    			arr[i] = 4
    		}
    	}()
    	wg.Wait()
    	fmt.Println(arr)
    }
    
    // go run .\collection_safety.go
    [4 3 4 3 4 3 4 3 4 3]
    

    并发修改slice

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    var arr = make([]int, 10)
    
    func main() {
    	wg := sync.WaitGroup{}
    	wg.Add(2)
    	go func() {
    		defer wg.Done()
    		for i := 1; i < 10; i += 2 {
    			arr[i] = 3
    		}
    	}()
    	go func() {
    		defer wg.Done()
    		for i := 0; i < 10; i += 2 {
    			arr[i] = 4
    		}
    	}()
    	wg.Wait()
    	fmt.Println(arr)
    }
    
    // go run .\collection_safety.go
    [4 3 4 3 4 3 4 3 4 3]
    

    并发修改struct

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    type Student struct {
    	Name string
    	Age  int
    }
    
    var student = Student{Name: "tom", Age: 18}
    
    func main() {
    	wg := sync.WaitGroup{}
    	wg.Add(2)
    	go func() {
    		defer wg.Done()
    		student.Name = "test1"
    	}()
    	go func() {
    		defer wg.Done()
    		student.Name = "test2"
    		student.Age = 22
    	}()
    	wg.Wait()
    	fmt.Println(student)
    }
    
    // go run .\collection_safety.go
    {test1 22}
    // go run .\collection_safety.go
    {test2 22}
    

    并发修改map

    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    var dict = make(map[int]string, 10)
    
    func main() {
    	wg := sync.WaitGroup{}
    
    	wg.Add(2)
    	go func() {
    		defer wg.Done()
    		dict[1] = "Tom"
    	}()
    	go func() {
    		defer wg.Done()
    		dict[1] = "jieke"
    	}()
    	wg.Wait()
    
    	for i, v := range dict {
    		fmt.Printf("%d: %s\n", i, v)
    	}
    }
    
    // 概率性报错
    // go run .\collection_safety.go
    fatal error: concurrent map writes
    
    goroutine 6949 [running]:
    runtime.throw(0x4d2ca3, 0x15)
            E:/Go/src/runtime/panic.go:1116 +0x79 fp=0xc000041f50 sp=0xc000041f20 pc=0x432f99
    runtime.mapassign_fast64(0x4b5d40, 0xc00006e330, 0x1, 0xc000050528)
            E:/Go/src/runtime/map_fast64.go:176 +0x311 fp=0xc000041f90 sp=0xc000041f50 pc=0x410461
    main.main.func1(0xc0000120a0)
            E:/GO_projectv/collection_safety.go:25 +0x75 fp=0xc000041fd8 sp=0xc000041f90 pc=0x4a0845
    runtime.goexit()
            E:/Go/src/runtime/asm_amd64.s:1373 +0x1 fp=0xc000041fe0 sp=0xc000041fd8 pc=0x45daa1
    created by main.main
            E:/GO_project/collection_safety.go:23 +0x8d
    
    goroutine 1 [semacquire]:
    sync.runtime_Semacquire(0xc0000120a8)
            E:/Go/src/runtime/sema.go:56 +0x49
    sync.(*WaitGroup).Wait(0xc0000120a0)
            E:/Go/src/sync/waitgroup.go:130 +0x6b
    main.main()
            E:/GO_project/collection_safety.go:31 +0xbd
    exit status 2
    

    使用sync.Map

    • var dict sync.Map 定义sync.Map
    • dict.Store(2,"jieke") 赋值
    • dict.Range 遍历
    package main
    
    import (
    	"fmt"
    	"sync"
    )
    
    var dict sync.Map
    
    func main() {
    	wg := sync.WaitGroup{}
    
    	for i := 0; i < 10000000; i++ {
    		wg.Add(2)
    		go func() {
    			defer wg.Done()
    			dict.Store(1, "tom")
    		}()
    		go func() {
    			defer wg.Done()
    			dict.Store(2, "jieke")
    		}()
    		wg.Wait()
    	}
    
    	dict.Range(func(key, value interface{}) bool {
    		fmt.Printf("%d: %s\n", key.(int), value.(string))
    		return true
    	})
    }
    
  • 相关阅读:
    洛谷 P1525 关押罪犯(并查集|二分图判定&二分答案)
    洛谷 P1948 [USACO08JAN]Telephone Lines S(贪心+最短路)
    洛谷 P1315 观光公交(贪心+模拟)
    洛谷 P3258 [JLOI2014]松鼠的新家(树上差分)
    【NOIP2001】统计单词个数
    【洛谷习题】皇后游戏
    【洛谷习题】木棍加工
    【SDOI2008】仪仗队
    【洛谷习题】末日的传说
    【洛谷习题】又是毕业季I
  • 原文地址:https://www.cnblogs.com/Otiger/p/16221792.html
Copyright © 2020-2023  润新知