Golang 解决 Map 并发读写安全问题
上面代码执行大概率出现报错:fatal error: concurrent map writes
<h2>二、问题分析</h2>网上关于 golang 编程中 map 并发读写相关的资料很多,但总是都说成 并发读写 造成上面的错误,到底是 并发读 还是 并发写 造成的,这个很多资料都没有说明。
我们把上面的案例分别在循环中注释 Read 和 Write 函数的调用,分别测试 并发读 和 并发写;
循环次数分别测试了 100、1 w、100 w 次,并发读操作绝对不会报上面的错,而并发写基本都会报错。
因此,这个错误主要原因是:map 并发写。
<h2>三、问题原因</h2>为什么 map 并发写会导致这个错误? 网络上的相关文章也大都有说明。
因为 map 变量为 指针类型变量,并发写时,多个协程同时操作一个内存,类似于多线程操作同一个资源会发生竞争关系,共享资源会遭到破坏,因此golang 出于安全的考虑,抛出致命错误:fatal error: concurrent map writes。
<h2>四、解决方案</h2>网上各路资料解决方案较多,主要思路是通过加锁保证每个协程同步操作内存。
github 上找到一个 concurrentMap 包,案例代码修改如下:
<pre><code class="lang-go hljs"><span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"fmt"</span> cmap <span class="token string">"github.com/orcaman/concurrent-map"</span> <span class="token string">"time"</span> <span class="token punctuation">)</span> <span class="token keyword">var</span> TestMap cmap<span class="token punctuation">.</span>ConcurrentMap <span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> TestMap <span class="token operator">=</span> cmap<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> i <span class="token operator">:=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token number">100</span><span class="token punctuation">;</span> i<span class="token operator"> </span> <span class="token punctuation">{</span> <span class="token keyword">go</span> <span class="token function">Write</span><span class="token punctuation">(</span><span class="token string">"aaa"</span><span class="token punctuation">,</span> <span class="token string">"111"</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token function">Read</span><span class="token punctuation">(</span><span class="token string">"aaa"</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token function">Write</span><span class="token punctuation">(</span><span class="token string">"bbb"</span><span class="token punctuation">,</span> <span class="token string">"222"</span><span class="token punctuation">)</span> <span class="token keyword">go</span> <span class="token function">Read</span><span class="token punctuation">(</span><span class="token string">"bbb"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> time<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">5</span> <span class="token operator">*</span> time<span class="token punctuation">.</span>Second<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">Read</span><span class="token punctuation">(</span>key <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> v<span class="token punctuation">,</span> ok <span class="token operator">:=</span> TestMap<span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span> ok <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"键值为 %s 的值为:%s"</span><span class="token punctuation">,</span> key<span class="token punctuation">,</span> v<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"键值不存在"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">Write</span><span class="token punctuation">(</span>key <span class="token builtin">string</span><span class="token punctuation">,</span> value <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> TestMap<span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span> <span class="token punctuation">}</span> </code></pre> <h2>五、思考总结</h2>因为我是以 PHP 打开的编程世界,PHP 语言只有单线程,且不涉及指针操作,变量类型也是弱变量,以 PHP 编程思维刚开始接触 Golang 时还比较容易上手,但越往后,语言的特性区别就体现得越来越明显,思维转变就越来越大,对我来说是打开了一个新世界。
像本文出现的错误案例,也是因为自己没有多线程编程的思维基础,导致对这种问题不敏感,还是花了蛮多时间理解的。希望对和我有相似学习路线的朋友提供到一些帮助。
到此这篇关于“Golang 解决 Map 并发读写安全问题”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!您可能感兴趣的文章:
Golang的map并发安全
golang key map 所有_谨慎使用golang中的map
golang 并发访问map遇到的问题
Golang map 并发读写问题源码分析
go二维map_Golang使用Map的正确姿势
golang map 锁_golang中线程安全的map
golang map 锁_Golang线程安全的map
Golang线程安全Map:sync.Map使用小结
golang中map的一些注意事项
golang map并发读写问题踩坑记录 `concurrent map read and map write`