ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

GO-并发安全

2022-05-04 18:00:57  阅读:192  来源: 互联网

标签:wg func 安全 fmt sync 并发 go GO main


资源竞争


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

  • 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
	})
}

标签:wg,func,安全,fmt,sync,并发,go,GO,main
来源: https://www.cnblogs.com/Otiger/p/16221792.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有