🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
> # 切片非线程安全,并发操作为啥不会像map一样报错 > # 切片的底层 ![](https://img.kancloud.cn/21/9a/219a435fe5956fe3183f7c3b27be2c9f_684x228.png) ![](https://img.kancloud.cn/20/52/20522c7e57cd03d740e44b3043fbc8a3_886x579.png) > # 切片与 map 的底层结构差异 * **切片**:切片的底层是一个包含三个元素的数据结构:指向底层数组的指针、切片的长度以及切片的容量。切片本身是对底层数组的一个“视图”。当你对切片进行操作时,主要是对这个底层数组的访问。即使多个 goroutine 访问同一个切片,Go 运行时不会进行额外的检查。 * **map**:`map` 是一个复杂的哈希表结构,内部包含哈希桶、哈希冲突解决机制等。当多个 goroutine 并发地对同一个 map 进行读写时,由于 map 需要对内部哈希桶进行管理(如扩容、rehash 等操作),一旦多个 goroutine 同时修改这些共享的哈希桶,Go 运行时会检测到数据竞争和一致性问题,因此直接抛出致命错误并终止程序运行。这也是为什么我们会看到类似 `fatal error: concurrent map writes` 的错误。 # 切片并发操作的行为 在切片的并发读写中,常见的行为有: * **数据竞争**:多个 goroutine 对切片进行并发读写操作时,Go 并不会进行额外的检查,所以即使发生了数据竞争,也不会抛出类似 `map` 的错误。数据竞争可能会导致切片的数据不一致,但不会直接导致程序崩溃。 * **扩容引发的问题**:切片在追加元素时,可能触发底层数组的扩容(即重新分配更大的内存空间,并复制原有数据)。在并发环境中,多个 goroutine 可能同时触发扩容,这可能导致数据丢失或出现未定义行为,但通常不会触发程序崩溃。 # Go 运行时错误检查机制的差异 Go 运行时对 `map` 的并发操作有明确的检查机制,因为 `map` 是一个更复杂的结构,并且在并发情况下更容易引发致命的不一致问题。Go 选择在运行时抛出错误,以提醒开发者这个并发问题。 相比之下,切片的并发访问没有类似的检查机制。虽然切片的并发操作也会引发数据不一致的问题,但这些问题不一定会导致程序立即崩溃,而是表现为数据错乱或不符合预期的结果。这也是为什么并发读写切片不会像 `map` 那样直接报错终止程序。