string是Go的内建类型,但对它的读写操作并非线程安全的,原因在于它的内部实际上是通过struct存储的,我们可以在runtime/string.go里面看到它的内部定义。
我们可以通过一个简单的测试代码看到结果
可以看到在频繁的写入操作中,另一协程可能读到部分写入的结果(len为1,指针指向“aa,或者是len为2,指针指向了”0“)。
细心的读者可能会发现在写入协程中有一个多余的sleep操作,如果把这个sleep去掉,运行的结果是永远读不到脏数据,这是为什么呢?原因在于编译器的优化,通过go tool compile -S -S可以发现,如果去掉Sleep,编译器将会优化了所有的写入操作。通过汇编源码,我们也可以更直观的看到一个简单的string赋值操作的过程。
在并发场景下,string跟interface一样,都是需要使用atomic包来保证读写的原子性。