golang的mutex的误用(defer)

前两天,我的一个golang程序突然出现异常,因为从ramq中读取数据并且自动ack, 然后另外一个线程再读取出来。 中间使用了mutex来防止冲突,结果发现突然不知道什么原因,这个数据竟然没有读到了。

后来分析应该是mutex的原因,然后做了个小测试,就简单的在使用mutex.Lock的后面直接defer mutex.Unlock, 本意很简单,在变量的生命周期结束后直接Unlock,结果发现竟然只会lock,不会unlock。将mutex.Unlock替换为 一个输出函数,结果发现也不会。仔细看了下defer的用法,结果发现defer是在函数结束之前调用的。最后把lock和 unlock以及中间的代码封装为一个内部函数,再次调用就正常了。

golang vs rust vs javascript

最近研究了几天rust,并不是特别的深入,主要用来做wasm。 之前的部分关键程序使用golang写的,但不是wasm,也简单封装了接口, 试了下wasm。

就体验而言,rust的学习曲线实在太抖,相比之前的golang学习,golang用个个把小时看下语法,结构,项目组织方式,然后就可以做 项目了。rust看了几天,貌似还在门外徘徊,不过没关系,经过几天的摸索,我终于学会了使用rust来编写wasm,并且和elementui的 框架的配合调整为我想要的了。而这个只是灾难的开始。

我想试着用rust,一个主要原因是golang生成的wasm实在太大,什么都不做的2MB+了。另外一个最重要的原因是golang生成的wasm, 在解析一个很大的json字符串的时候,竟然需要13+秒的时间,如果使用第三方的json解析的话,需要1+秒的时间,虽然可以使用tinygo 来减少wasm的容量,但tinygo无法使用第三方的json解析。而在查看wasm相关信息的时候,rust据说不错。所以就决定写个试试了。

下面的坑都是根据回忆记录的。不一定是对的,所有执行的目标都是wasm

  1. rust的json解析,如果不使用js_sys的json解析的话,需要很长的时间,没记错的话,需要很久,至少10s+是有的。这个json数据大约在几十MB之间。 如果使用js_sys的话,需要0.8秒左右的时间。之所以提出来是因为太明显了。最后逼迫我必须使用js_sys的解析。缺点就是描写的各种累心
  2. 因为1解析出来的json数据会被各个不同的部分使用,因此这个变量要被全局存储。全局存储大概搞了我好多天,因为没有最理想的。 刚开始准备一个最简单的Option的方案,不支持指针类的; 升级为lazy_static方案, 不支持js_sys::JsValue,JsonValue太慢 放弃; thread_local方案,这个需要使用with,这个是我目前可以找到的可以支持js_sys::JsValue的了,也只能这样用了,使用with, 丑就丑吧
  3. 因为2使用了with方案,而代码中的数据在处理的时候,会从其他地方得到的数据进行整合,比如我又从其他的公共变量获取数据,而 with的处理结果,除非使用clone的方式,否则由于变量生命期的关系,各种编译报错,我真的恨死rust的这种反人类的设计了。 明明我只是为了读取内部的一段内存的数据,结果非的逼我clone出一段内存来,感情现在内存真的都是白菜价啊
  4. 很无奈,其他部分(我只测试了一个,以后的以后想办法对付吧),我使用了lazy_static方案,我试了下Mutex,结果在浏览器里 一跑,发现浏览器cpu 100%卡住了,等好久才有反映,这下好了,整个数据不用处理了。既然Mutex不行,就试试RwLock,反正我 数据仍到HashMap中,更新少,查询多,但要返回相应的数据,还得clone,于是又回到3了。
  5. 我无数次准备掐死自己的心都有了,让你犯贱选rust,你看使用golang多少,一周的rust研究成功,golang两个小时就达到相同效果了。 但golang真的慢啊
  6. rust的设计初衷或许非常完美,但智能指针的设计模式或者所有权的设计,真的是非人类。即使是学院的erlang,也没有rust这么变态, 我感觉我好久都没关注过erlang了,真有点怀念熟悉了之后处理代码的那种感觉。

golang 类型转换的陷阱

今天在调试索引色图片的时候,使用golang碰到一个很奇怪的事情, 代码如下:


var pal_data []byte //调色板数据
var img_data []byte //图像数据
new_img := image.NewRGBA(image.Rect(0, 0, Width, Height))
for x:=0; x < len(img_data); x++ {
  v := img_data&#91;x&#93;
  if(v == 106) {
    vv:=106
    fmt.Println("V1=",v,",C={",pal_data&#91;vv*3+2&#93;,",",pal_data&#91;vv*3+1&#93;, ",",pal_data&#91;vv*3&#93;, "}")
    fmt.Println("V2=",v,",C={",pal_data&#91;v*3+2&#93;,",",pal_data&#91;v*3+1&#93;, ",",pal_data&#91;v*3&#93;, "}")
  }
}

&#91;/sourcecode&#93;

<p> 我特别提取了一个索引值106,结果发现打印出来的颜色值是不同的,使用vv这个指定变量的值是正确的, 另外一个却是错误的。 </p>

<p> 我一开始以为是优化的问题,然后取消了所有优化,发现结果还是不同的。 </p>

<p> 程序没有任何警告之类的,除了结果是错误的,其他的都正常。 </p>

<p> 直到我修改成下面的样子: </p>



v := img_data[x]*3
if(v == 106*3) 

在其他语言中,最多会有个警告,在golang中,直接报错了:

constant 318 overflows byte

…. 使用golang 的语法糖果然是需要代价的,当v:=img_data[x]的时候,v的类型是byte,标示范围为 0-255,然后v*3还是个byte,计算结果标示的范围还是0-255,并不会自动转换为容量更大的short或者int, 所以计算到最后还是个byte, 远远没办法访问所有的调色板数据了。

所以将v:=img_data[x]修改喂v:=int(img_data[x])就没问题了

类型很重要,当使用简单写法的时候尤其需要注意。