Go并发读取string的Panic问题

news2024/11/19 8:51:23

在这里插入图片描述

上问题,先看下panic的函数栈信息,说现实strings.Count()发生了panic,来看下函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygXDobAe-1675330949311)(assets/IMG_15.png)]

第一个参数是字符串s,再结合函数栈信息的十六进制,0x00x9表示字符串s的地址和长度

这里来看一下string的底层数据结构:
在这里插入图片描述

简单的结构,str是字符串的首地址,len是字符串的长度,string的数据结构与切片相似。struct的赋值并非是并发安全的,所以问题的现象也容易解释

0x0表示nil,但上一个字符串的长度为0x9,在读取或复制这个字符串的时候,刚好另一个goRoutine只更改了str没有修改len,这时候会出现上述现象:空字符串的长度为9,最终在bytealg.CountString()发生panic。

写个Demo复现一下

const (
	BusinessOne = "what's up"
	BusinessTwo = ""
)

func split(s string) {
	fmt.Printf("%+v\n", *(*reflect.StringHeader)(unsafe.Pointer(&s)))
	ss := strings.Split(s, ",")
	fmt.Println(ss)
}

func main() {

	var str string

	go func() {
		flag := false
		for {
			if flag {
				str = BusinessOne
			} else {
				str = BusinessTwo
			}
			time.Sleep(10)
			flag = !flag
		}
	}()

	for {
		split(str)
		time.Sleep(10)
	}
}

StringHeaderstring的数据结构是一样的,只不过参数可以导出,方便打印

在这里插入图片描述

这个Demo也可以做一些修改, 比如在goRoutine中反复给s赋值长度不同且非零的字符串,然后再main中打印,就会发现长字符串偶现被截断的情况,被截断的长度正好是短字符串的长度。将上个Demo中BusinessTwo修改即可

典型数据竟态 Case对应第四种

  • 循环内并发竟态计数
  • 意外共享变量
  • 为保护全局变量读写(通常是map并发安全问题)
  • 一些原子类型(通常是在结构体内变量并发读写)
  • 向关闭通道发送数据

解决方案:
A typical fix for this race is to use a channel or a mutex. To preserve the lock-free behavior, one can also use the sync/atomic package.

// error case
type Watchdog struct{ last int64 }

func (w *Watchdog) KeepAlive() {
	w.last = time.Now().UnixNano() // First conflicting access.
}

func (w *Watchdog) Start() {
	go func() {
		for {
			time.Sleep(time.Second)
			// Second conflicting access.
			if w.last < time.Now().Add(-10*time.Second).UnixNano() {
				fmt.Println("No keepalives for 10 seconds. Dying.")
				os.Exit(1)
			}
		}
	}()
}

// ok
// 通过sync/atomic包无锁
type Watchdog struct{ last int64 }

func (w *Watchdog) KeepAlive() {
	atomic.StoreInt64(&w.last, time.Now().UnixNano())
}

func (w *Watchdog) Start() {
	go func() {
		for {
			time.Sleep(time.Second)
			if atomic.LoadInt64(&w.last) < time.Now().Add(-10*time.Second).UnixNano() {
				fmt.Println("No keepalives for 10 seconds. Dying.")
				os.Exit(1)
			}
		}
	}()
}

然后开始研究本次问题的解决方案。本次问题出现的原因是string的修改并非是原子操作,与int、bool等不同,所以与数据竟态给的Demo不尽相同。大体思路是一致的,通过atomic的Store方法修改值,Load方法获取值,这样能够保证在split方法中打印出的指针内容一致。附上代码:
tmp用于获取string的指针地址

package main

import (
	"fmt"
	"reflect"
	"sync/atomic"
	"time"
	"unsafe"
)

const (
	BusinessOne = "what's up"
	BusinessTwo = ""
)

var PL *string

func split(s string) {
	fmt.Printf("%+v\n", *(*reflect.StringHeader)(unsafe.Pointer(&s)))
	tmp := (*unsafe.Pointer)(unsafe.Pointer(&PL))
	str := (*string)(atomic.LoadPointer(tmp))
	fmt.Println(str)
}

func main() {

	var str string
	str = BusinessOne
	go func() {
		flag := true
		for {
			if flag {
				str = BusinessOne
				tmp := (*unsafe.Pointer)(unsafe.Pointer(&PL))
				atomic.StorePointer(tmp, unsafe.Pointer(&str))
			} else {
				str = BusinessTwo
				tmp := (*unsafe.Pointer)(unsafe.Pointer(&PL))
				atomic.StorePointer(tmp, unsafe.Pointer(&str))
			}
			time.Sleep(10)
			flag = !flag
		}
	}()

	for {
		split(str)
		time.Sleep(10)
	}
}

在这里插入图片描述

可以看到string数据结构的一致性以及str地址的一致性,完结啦

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/194888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Security OAuth2.0认证授权

目录 1.基本概念 1.1什么是认证 1.2什么是会话&#xff1f; 1.2什么是授权 1.3授权的数据模型 1.4.1基于角色的访问控制 1.4.2基于资源的访问控制 2.基于Session的认证方式 2.1认证流程 分布式系统认证方案 什么事分布式系统&#xff1f; 分布式认证需求 分布式认证…

行业安全解决方案 | 能源行业如何在新时期建设新安全?

伴随5G、人工智能、大数据、云计算等新技术的蓬勃发展&#xff0c;数智化成为传统电力能源转型发展的重要方向。与此同时&#xff0c;伴随着能源行业数字技术与电力技术、业务生产的愈发深度的融合&#xff0c;新时期的能源行业网络安全形势有了新变化&#xff0c;网络边界威胁…

DPDK实现的用户态协议栈(UDP)

DPDK实现的用户态协议栈背景NIC与DPDK的比较环境配置Windowe下配置静态IP表代码实现总结背景 DPDK接管NIC之后&#xff0c;接收到的数据都是原始数据&#xff0c;要实现一个协议栈就必须解析协议包和打包协议包&#xff0c;DPDK提供了丰富的API可以使用。 以UDP协议为例&#…

redis分布式集群

文章目录一、redis持久化1.1.RDB持久化1.1.1.执行时机1.1.2.RDB原理1.1.3.小结1.2.AOF持久化1.2.1.AOF原理1.2.2.AOF配置1.2.3.AOF文件重写1.2.4.小结1.3.RDB与AOF对比二、Redis主从集群2.1.集群结构2.2.准备实例和配置2.3.启动2.4.开启主从关系2.5.测试2.6.主从数据同步原理2.…

MMLAB学习笔记-DAY1

一、机器学习 1.机器学习的典型范式 监督学习&#xff1a;数据是由人工标注的&#xff0c;数据之间存在某种映射关系&#xff0c;目的是让机器学习到数据和标签之间的关系无监督学习&#xff1a;数据是没有标签的&#xff0c;通过对数据分析&#xff0c;运用聚类等方法探索出…

六、循环语句

一、while循环 1.语法 while 条件:条件成⽴重复执⾏的代码1条件成⽴重复执⾏的代码2.....2.应用 #偶数累加 i 1 resualt 0while i<100:if i % 2 0:resualt ii1print(resualt)3.break和continue 说明&#xff1a; 举例&#xff1a;⼀共吃5个苹果&#xff0c;吃完第⼀个&…

如何又快又好实现 Catalog 系统搜索能力?火山引擎 DataLeap 这样做

摘要 DataLeap 是火山引擎数智平台 VeDI 旗下的大数据研发治理套件产品&#xff0c;帮助用户快速完成数据集成、开发、运维、治理、资产、安全等全套数据中台建设&#xff0c;降低工作成本和数据维护成本、挖掘数据价值、为企业决策提供数据支撑。 火山引擎 DataLeap 的 Data…

Spring Boot + WebSocket 实时监控异常

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

【笔记】容器基础-隔离与限制

Docker 项目的核心原理&#xff1a;为待创建的用户进程 1.启用 Linux Namespace 配置&#xff1a;修改进程视图 2.设置指定的 Cgroups 参数&#xff1a;为进程设置资源限制 3.切换进程的根目录&#xff08;Change Root&#xff09;&#xff1a; 容器的隔离与限制 1.启用 Linux…

MySQL性能优化四 MySQL索引优化实战一

一 查询案例 示例表 CREATE TABLE employees (id int(11) NOT NULL AUTO_INCREMENT,name varchar(24) NOT NULL DEFAULT COMMENT 姓名,age int(11) NOT NULL DEFAULT 0 COMMENT 年龄,position varchar(20) NOT NULL DEFAULT COMMENT 职位,hire_time timestamp NOT NULL DEF…

王凤英,能治好何小鹏的技术“自恋”吗?

1月30日&#xff0c;小鹏官宣一手打造长城汽车(601633)SUV战略转型的前二号人物——王凤英&#xff0c;加盟小鹏出任CEO一职。 虽然这则消息已风传多日&#xff0c;但正式公布的一刻还是在汽车圈内炸开了锅&#xff0c;主要原因有两点&#xff1a;一是王凤英刚刚加入小鹏就被委…

2-1JVM内存分析

一、java类的生命周期 1.加载(把class文件的数据加载到jvm内存的元空间) 2.连接验证 验证语法是否正确准备 给静态变量做内存分配和默认值分配识别 解析常量池 3.初始化静态变量赋初始值静态代码块执行 4.使用(被jvm使用) 5.卸载(如果在程序中没有再使用到这个类,这个类会被从…

跳槽前恶补面试题,成功上岸华为,拿到33k的测开offer

不知不觉间&#xff0c;时间过得真快啊。作为一名程序员&#xff0c;应该都清楚每年的3、4月份和9、10月份都是跳槽的黄金季&#xff0c;各大企业在这段时间会大量招聘人才。在这段时间里&#xff0c;有人欢喜有人悲。想必各位在跳槽前都会做好充足的准备&#xff0c;同样做足了…

还是你厉害啊,用 Python 下载高清视频真速度

今天我们来进行 Python 爬虫实战&#xff0c;学以致用嘛&#xff0c;这也是咱们不断学习的动力&#xff01; 我们要爬取的网站是YY直播&#xff0c;不知道有多少朋友知道&#xff0c;反正小编以前是不知道的&#xff0c;真的不知道~ 那么为什么我们选择这个网站呢&#xff0…

【5.1】Nacos注册中心--认识和安装Nacos/快速入门

【5.1】Nacos注册中心--认识和安装Nacos/快速入门1 认识Nacos2 安装Nacos3 服务注册到Nacos4 总结1 认识Nacos Nacos是阿里巴巴的产品&#xff0c;现在是SpringCloud中的一个组件。相比Eureka功能更加丰富&#xff0c;在国内受欢迎程度较高。 2 安装Nacos 建议大家下载Typora之…

Grafana 系列文章(五):Grafana Explore 查询管理

&#x1f449;️URL: https://grafana.com/docs/grafana/latest/explore/query-management/ &#x1f4dd;Description: Explore 中的查询管理 为了帮助调试查询&#xff0c;Explore 允许你调查查询请求和响应&#xff0c;以及查询统计数据&#xff0c;... Explore 中的查询管理…

CTFshow_萌新--密码篇

一、萌新认证进群大喊萌新码&#xff0c;即可获得。。。。。二、萌新密码1密文&#xff1a;53316C6B5A6A42684D3256695A44566A4E47526A4D5459774C5556375A6D49324D32566C4D4449354F4749345A6A526B4F48303D并给上了一下工具包。①密文首先Hex解码得到串&#xff1a;S1lkZjBhM2Vi…

项目管理工具——Maven

目录儿一、Maven简介二、下载与安装环境配置三、Maven基础概念3.1 仓库3.2 坐标在中央仓库网获取依赖坐标3.3 本地仓库配置3.4 远程仓库配置一、Maven简介 Maven是用java语言编写的。Maven的本质是一个项目管理工具&#xff0c;将项目开发和管理过程抽象成一个项目对象模型(PO…

商城项目的表设计

零、前言 1、优惠卷设计 电商项目中的优惠券系统这样设计&#xff0c;同事直呼 666 &#xff01; 2、SPU和SKU的定义及他们之间的关系 SPU全称Standard Product Unit&#xff0c;即标准化产品单元。 简单理解就是某一种产品。 SKU全称Stock Keeping Unit&#xff0c;即库存量…