GoLang切片并发安全解决方案详解

10-12 175阅读 0评论

?=1.介绍切片并发问题

关于切片的,Go语言中的切片原生支持并发吗?

2.实践检验真理

实践是检验真理的唯一标准,所以当我们遇到一个不确定的问题,直接写demo来验证,因为切片的特点,我们可以分多种情况来验证

1.不指定索引,动态扩容并发向切片添加数据

2.指定索引,指定容量并发向切片添加数据

不指定索引,动态扩容并发向切片添加数据

不指定索引,动态扩容并发向切片添加数据:

通过打印数据发现每次len与cap的结果都不一致

func concurrentAppendSlicenotforceIndex() { 	sl := make([]int, 0) 	wg := sync.WaitGroup{} 	for index := 0; index < 100; index++ { 		k := index 		wg.Add(1) 		Go func(num int) { 			sl = append(sl, num) 			wg.Done() 		}(k) 	} 	wg.Wait() 	fmt.Println(sl) 	fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) } func main() { 	concurrentAppendSliceNotForceIndex() 	/*第一次运行代码后,输出:[2 0 1 5 6 7 8 9 10 4 17 11 12 13 14 15 16 21 18 19 20 23 22 24 25 26 39 27 28 29 30 31 35 55 54 56 57 58 59 60 61 62 64 63 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 86 91 92 93 94 96 95 97 98 99] 	final len(sl)=74 cap(sl)=128*/ 	//第二次运行代码后,输出:省略切片元素输出... final len(sl)=81 cap(sl)=128 	//第二次运行代码后,输出:省略切片元素输出... final len(sl)=77 cap(sl)=128 }
指定索引,指定容量并发向切片添加数据

指定索引,指定容量并发向切片添加数据:

通过结果我们可以发现符合我们的预期,长度和容量都是100

func concurrentAppendSliceForceIndex() { 	sl := make([]int, 100) 	wg := sync.WaitGroup{} 	for index := 0; index < 100; index++ { 		k := index 		wg.Add(1) 		GO func(num int) { 			sl[num] = num 			wg.Done() 		}(k) 	} 	wg.Wait() 	fmt.Println(sl) 	fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) } func main() { 	concurrentAppendSliceForceIndex() 	/*第一次运行代码后,输出:[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 7 	9 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99] 	final len(sl)=100 cap(sl)=100*/ 	/*第一次运行代码后,输出:[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 7 	9 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99] 	final len(sl)=100 cap(sl)=100*/ 	/*第一次运行代码后,输出:[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 7 	9 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99] 	final len(sl)=100 cap(sl)=100*/ } 

3.回答切片并发安全问题

我们都知道切片是对数组抽象,其底层就是数组,在并发下写数据到相同的索引位会被覆盖,并且切片也有自动扩容的功能,当切片要进行扩容时,就要替换底层的数组,在切换底层数组时,多个goroutine是同时运行的,哪个goroutine先运行是不确定的,不论哪个goroutine先写入内存,肯定就有一次写入会覆盖之前的写入,所以在动态扩容时并发写入数组是不安全的;

所以当别人问你slice支持并发时,你就可以这样回答它:

当指定索引使用切片时,切片是支持并发读写索引区的数据的,但是索引区的数据在并发时会被覆盖的;当不指定索引切片时,并且切片动态扩容时,并发场景下扩容会被覆盖,所以切片是不支持并发的~。

4.解决切片并发安全问题方式

针对上述问题,我们可以多种方法来解决切片并发安全的问题:

1.加互斥锁

2.使用channel串行化操作

3.使用sync.Map代替切片

5.附

设置为1的的时候,runtime.GOMAXPROCS(1)

package main import ( 	"fmt" 	"runtime" 	"sync" ) func concurrentAppendSliceNotForceIndex() { 	sl := make([]int, 0) 	wg := sync.WaitGroup{} 	for index := 0; index < 100; index++ { 		k := index 		wg.Add(1) 		go func(num int) { 			sl = append(sl, num) 			wg.Done() 		}(k) 	} 	wg.Wait() 	fmt.Println(sl) 	fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) } func main() { 	runtime.GOMAXPROCS(1) 	concurrentAppendSliceNotForceIndex() 	/* 		[99 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 		 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 5 		5 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 		82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98] 		final len(sl)=100 cap(sl)=128 	*/ 	/* 		[13 0 1 2 3 4 5 6 7 8 9 10 11 12 99 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 		 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 5 		5 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 		82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98] 		final len(sl)=100 cap(sl)=128 	*/ 	/* 		[10 0 1 2 3 4 5 6 7 8 9 99 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 		 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 5 		5 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 		82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98] 		final len(sl)=100 cap(sl)=128 	*/ } 
package main import ( 	"fmt" 	"runtime" 	"sync" ) var wg sync.WaitGroup var sl []int func add() { 	for index := 0; index < 100; index++ { 		sl = append(sl, index) 	} 	wg.Done() } func main() { 	runtime.GOMAXPROCS(1) 	wg.Add(1) 	go add() 	wg.Wait() 	//无论执行多少次都输出一下结果 	fmt.Println(sl) 	fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) 	/* 		[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 6 		3 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99] 		final len(sl)=100 cap(sl)=128 	*/ }
package main import ( 	"fmt" 	"runtime" 	"sync" ) var wg sync.WaitGroup var sl []int func add() { 	for index := 0; index < 50; index++ { 		sl = append(sl, index) 	} 	wg.Done() } func main() { 	runtime.GOMAXPROCS(1) 	wg.Add(2) 	go add() 	go add() 	wg.Wait() 	//无论执行多少次都输出一下结果 	fmt.Println(sl) 	fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) 	/* 			[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 		 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49] 		final len(sl)=100 cap(sl)=128 	*/ }

不限数量:

package main import ( 	"fmt" 	"sync" ) var wg sync.WaitGroup var sl []int func add() { 	for index := 0; index < 50; index++ { 		sl = append(sl, index) 	} 	wg.Done() } func main() { 	wg.Add(2) 	go add() 	go add() 	wg.Wait() 	fmt.Println(sl) 	fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) 	/* 				[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 		 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49] 		final len(sl)=82 cap(sl)=128 	*/ }

加锁

package main import ( 	"fmt" 	"sync" ) var wg sync.WaitGroup var sl []int var lock sync.Mutex func add() { 	for index := 0; index < 50; index++ { 		lock.Lock() 		sl = append(sl, index) 		lock.Unlock() 	} 	wg.Done() } func main() { 	wg.Add(2) 	go add() 	go add() 	wg.Wait() 	fmt.Println(sl) 	fmt.Printf("final len(sl)=%d cap(sl)=%d\n", len(sl), cap(sl)) 	/* 		[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 		17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49] 		final len(sl)=100 cap(sl)=128  	*/ 	/* 		[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 		 30 31 32 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 		 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 33 34 35 3 		6 37 38 39 40 41 42 43 44 45 46 47 48 49] 		final len(sl)=100 cap(sl)=128 	*/ } 

到此这篇关于golang切片并发安全解决方案详解的文章就介绍到这了,更多相关Golang切片并发安全内容请搜索云初冀北以前的文章或继续浏览下面的相关文章希望大家以后多多支持云初冀北!

免责声明
本站提供的资源,都来自网络,版权争议与本站无关,所有内容及软件的文章仅限用于学习和研究目的。不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负,我们不保证内容的长久可用性,通过使用本站内容随之而来的风险与本站无关,您必须在下载后的24个小时之内,从您的电脑/手机中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。侵删请致信E-mail:goliszhou@gmail.com
$

发表评论

表情:
评论列表 (暂无评论,175人围观)

还没有评论,来说两句吧...