慎写指针类型的全局变量

news2024/11/24 16:05:32

简述:


关于range二三事[1] 第二个case中,介绍了对于指针类型的 切片/map变量A 的循环,要格外注意, 迭代出的value作用域是整个方法而非循环体内.

改进办法:在循环体中引入中间变量,"暂存"下每次迭代的value的值


但对于这个A,如果是全局变量,则又极有可能出现问题:

package main

import (
 "fmt"
)

type UserInfo struct {
 Name string
 Age  int
}

var (
 defaultInfo    = UserInfo{Name: "fliter", Age: 26}
 defaultInfoSli = []*UserInfo{&defaultInfo}
)

func main() {
 
 for _, v := range defaultInfoSli {
  tmp := v

  //go func() {
  tmp.Age = 100
  //}()
 }
 //time.Sleep(1e9)
 fmt.Println(defaultInfoSli[0].Age)
}

defaultInfoSli迭代出的v为指针类型,tmp仍为指针类型,对其赋值,会改变全局变量defaultInfoSli的值




复现:


在具体业务场景中,服务启动时初始化(取数据库或redis,或读取配置文件,加载到内存中)了一个全局变量.每个http请求过来,golang都会有一个新的协程去处理相关逻辑. 对于某个具体方法内的变量,对每次请求都是独立和隔离(每次请求都相当于一个个cellar,彼此之间不会有干涉和影响), 但对于永久存在内存中的全局变量,如果有对其写操作,每次请求都会影响该全局变量. 当出现并发请求如用户x和y同时请求接口, 两次请求都会改写全局变量, 这时就很可能出现返回的x和y的数据错乱


Demo如下:

package main

import (
 "encoding/json"
 "fmt"
 "github.com/davecgh/go-spew/spew"
 "log"
 "net/http"
)

type BookInfo struct {
 Title string
 Rank  int
 Data  interface{}
}

var (
 defaultBook1 = BookInfo{Title: "水浒传", Rank: 1}
 defaultBook2 = BookInfo{Title: "三国演义", Rank: 2}
 defaultBook3 = BookInfo{Title: "西游记", Rank: 3}
 defaultBook4 = BookInfo{Title: "红楼梦", Rank: 4}

 DefaultBookSli = []*BookInfo{&defaultBook1, &defaultBook2, &defaultBook3, &defaultBook4}
)

type CommonParams struct {
 ID   int64
 Name string
}

var (
 ModuleHandlers = map[int]func(params *CommonParams) *BookInfo{
  1: HandleTypeOne,
  2: HandleTypeTwo,
  3: HandleTypeThree,
  4: HandleTypeFour,
 }
)

func main() {

 fmt.Println(DefaultBookSli)

 http.HandleFunc("/index", deal) //设置访问的路由

 err := http.ListenAndServe(":80"nil//设置监听的端口
 if err != nil {
  log.Fatal("ListenAndServe: ", err)
 }

}

func deal(w http.ResponseWriter, r *http.Request) {

 name := r.URL.Query().Get("name")

 //fmt.Println("name值为:", name)

 par := &CommonParams{
  ID:   0,
  Name: name,
 }

 // 获取相关数据
 for _, v := range DefaultBookSli {

  module := v

  //fmt.Println("module is:", module)
  //fmt.Println("排序为:", module.Rank)

  m := ModuleHandlers[module.Rank](par "module.Rank")

  // 填充模块数据
  if m.Data != nil {
   module.Data = m.Data
  }

  // (如果需要),重写模块标题(在此不需要)
  //if m.Title != "" {
  // module.Title = m.Title
  //}
 }

 //time.Sleep(1e9) //此处等待并不是因为协程,而是方便测试,不加这个等待,执行100次秒速就完成. 加这个等待是为了方便模拟"几个用户同时请求"
 //fmt.Println(DefaultBookSli[0].Rank)

 spew.Dump(DefaultBookSli)

 rsJson, _ := json.Marshal(DefaultBookSli)

 //fmt.Println(string(rsJson))    //这个写入到w的是输出到客户端的
 fmt.Fprintf(w, string(rsJson)) //这个写入到w的是输出到客户端的

}

func HandleTypeOne(p *CommonParams) *BookInfo {

 res := ""
 if p.Name == "施耐庵" {
  res = "我叫施耐庵,我是作者!"
 }
 return &BookInfo{Data: res}
}

func HandleTypeTwo(p *CommonParams) *BookInfo {

 res := ""
 if p.Name == "罗贯中" {
  res = "我叫罗贯中,我是作者!"
 }
 return &BookInfo{Data: res}
}

func HandleTypeThree(p *CommonParams) *BookInfo {

 res := ""
 if p.Name == "吴承恩" {

  res = "我叫吴承恩,我是作者!"
 }
 return &BookInfo{Data: res}
}

func HandleTypeFour(p *CommonParams) *BookInfo {

 res := ""
 if p.Name == "曹雪芹" {
  res = "我叫曹雪芹,我是作者!"
 }

 return &BookInfo{Data: res}
}


带着参数x, 使用Postman进行串行调用[2]100次,

alt

同时再访问这个接口,带参数y,此时可以发现,出现了数据错乱:

alt



修改方案:


module := v这一步,实际上module依然是指针类型.

可以module := *v,这样module就不是指针类型,也就不会出现如上问题.


当时问题紧急,直接在里面新加了一个临时变量,即:

 // 获取相关数据
 for _, v := range DefaultBookSli {

  module := v

  var temModule = &BookInfo{
   Title: module.Title,
   Rank:  module.Rank,
  }
  
  m := ModuleHandlers[temModule.Rank](par "temModule.Rank")

  // 填充模块数据
  if m.Data != nil {
   module.Data = m.Data
  }
 }



详细过程参见 私有笔记 并发写全局变量导致的数据错乱问题[3],印象深刻的一次体验

参考资料

[1]

关于range二三事: https://dashen.tech/2018/10/03/%E5%85%B3%E4%BA%8Erange%E4%BA%8C%E4%B8%89%E4%BA%8B/

[2]

Postman进行串行调用: https://www.cnblogs.com/stm32stm32/p/10434399.html

[3]

并发写全局变量导致的数据错乱问题: https://note.youdao.com/web/#/file/WEB058d4e136c3ee281320806fd45e3b07a/note/WEB6c29aebf58ba0868bdeef60f7e6bac40/

本文由 mdnice 多平台发布

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

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

相关文章

Apache Maven简介安装及系统坏境配置eclipse配置Apache Maven---详细介绍

一,简介 Maven可以简化项目的构建和依赖管理,并提供了一种规范化和可复用的方式来管理Java项目。它广泛应用于Java开发领域,简单来说:它提供了一个简单而强大的方式来管理项目的构建、依赖关系和文档在企业级项目中被广泛采用。 1…

京东秋招攻略,备考在线测评和网申笔试

京东秋招简介 伴随着社会竞争越来越激烈,人们投递简历的岗位也变得越来越多元,而无论人们的选择面变成何样,那些知名度较高的企业,永远都备受关注,只要其一发布招聘公告,总有人第一时间踊跃报名。而作为这…

Java算法_ LRU 缓存(LeetCode_Hot100)

题目描述:请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 获得更多?算法思路:代码文档,算法解析的私得。 运行效果 完整代码 import java.util.HashMap; import java.util.Map;/*** 2 * Author: L…

winform中嵌入cefsharp, 并使用selenium控制

正常说, 需要安装的包 下面是所有的包 全部代码 using OpenQA.Selenium.Chrome; using OpenQA.Selenium; using System; using System.Windows.Forms; using CefSharp.WinForms; using CefSharp;namespace WindowsFormsApp2 {public partial class Form1 : Form{//…

(kubernetes)k8s常用资源管理

目录 k8s常用资源管理 1、创建一个pod 1)创建yuml文件 2)创建容器 3)查看所有pod创建运行状态 4)查看指定pod资源 5)查看pod运行的详细信息 6)验证运行的pod 2、pod管理 1)删除pod 2…

搜索二叉树(二叉树进阶)

目录 1.二叉搜索树 1.1二叉搜索树概念 1.2二叉搜索树操作 2.3二叉搜索树的实现 2.4二叉搜索树的应用 2.5二叉搜索树的性能分析 1.二叉搜索树 1.1二叉搜索树概念 二叉搜索树又称二叉排序树,它或者是一颗空树,或者是具有以下性质的二叉树&#xff…

mac安装nvm管理工具遇到的问题和解决方法

nvm 是一款可以管理多版本node的工具,因为是刚买没多久的电脑之前用的都是windows,昨天折腾了一下午终于倒腾好了 第一步: 卸载电脑已有的node;访问nvm脚本网址,另存为到电脑上任何目录,我是放在桌面上的…

OSPF技术入门(第三十四课)

1 OSPF的介绍 OSPF是一种链路状态路由协议,主要用于IP网络中的路由选择。它是一种开放协议,能够在不同的网络设备之间进行通信。OSPF利用链路状态数据库来描述网络拓扑结构,并通过Dijkstra算法计算出最短路径。它支持按照精确度划分的路由优先级,以及多个相等的路径,并能自…

微服务分布式搜索引擎 ElasticSearch 查询文档

文章目录 ⛄引言一、DSL查询文档⛅DSL 查询分类 二、DSL查询实例⛅全文检索查询⏰精确查询⚡地理坐标查询⌚复合查询 ⛵小结 ⛄引言 本文参考黑马 分布式Elastic search Elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海…

服务器数据恢复-断电导致ext4文件系统文件丢失的数据恢复案例

服务器数据恢复环境: 一台服务器挂载一台存储设备,存储中划分一个Lun;服务器操作系统是Linux centos,EXT4文件系统。 服务器故障&分析: 意外断电导致服务器操作系统无法启动,系统在修复后可以正常启动&…

竞赛项目 深度学习的动物识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

在Ubuntu中使用Docker启动MySQL8的天坑

写在前面 简介: lower_case_table_names 是mysql设置大小写是否敏感的一个参数。 1.参数说明: lower_case_table_names0 表名存储为给定的大小和比较是区分大小写的 lower_case_table_names 1 表名存储在磁盘是小写的,但是比较的时候是不区…

CMAKE生成exe文件时运行时有cmd窗口

1、运行exe执行文件 会有cmd弹窗 2、解决方法 只需要在cmakelists.txt中添加set(CMAKE_CXX_FLAGS “-mwindows”) 或者在cmake时指定编译参数cmake -DCMAKE_CXX_FLAGS"-mwindows"即可 如果用的是c而不是c,就只需把CXX改为C 重新编译打包运行后没有cmd弹…

centos自动同步北京时间

1、安装ntpdate服务 yum -y install ntpdate 2、加入自动任务计划 查找ntpdate的路径: which ntpdate 复制这个路径。 编辑自动任务计划并加入ntpdate: crontab -e # 每小时第30分钟同步AD域控时间 30 * * * * /usr/sbin/ntpdate -u 192.168.2.8 > …

超低成本FPGA JTAG方案

今天给大家带来一款超低成本的FPGA JTAG方案,硬件核心是用树莓派Pico,使用相关芯片自己制作JTAG则非常便宜,RP2040某宝的报价只有4元,所以自己制作成本非常低廉,当然使用Pico成本也不是很高,所以今天就以Pi…

ChineseChess

外卖中国象棋的梗。 外卖免单题: 如图,红棋先行,至少几步绝杀黑房(黑房尽量不让自己输)? ChineseChess.java 【帅】是左出还是右出,取决于,上图黑方那边的【士】 如图&#xff0c…

datax抽取库名带点的表遇到的问题

一、描述任务 使用Datax抽取mysql中的数据到hive的wedw_ods层中,mysql的库名为:b.p.n.p 表名为:bene_group 二、datax.json脚本生成 因为datax的脚本是自动生成的,生成的格式如下: {"core": {},"jo…

竞赛项目 深度学习手势识别算法实现 - opencv python

文章目录 1 前言2 项目背景3 任务描述4 环境搭配5 项目实现5.1 准备数据5.2 构建网络5.3 开始训练5.4 模型评估 6 识别效果7 最后 1 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 深度学习手势识别算法实现 - opencv python 该项目较为新颖…

中国信息安全测评中心CISP家族认证一览

随着国家对网络安全的重视,中国信息安全测评中心根据国家政策、未来趋势、重点内容陆续增添了很多CISP细分认证。 今日份详细介绍,部分CISP及其子品牌相关认证内容,一定要收藏哟! 校园版CISP NISP国家信息安全水平考试&#xff…

如何用SOLIDWORKS Simulation 避免共振现象

零件都有它的固有振动频率,称之为共振频率。当零部件的固有频率和激励频率相近时,对零部件的破坏是非常严重的,这就是我们说的共振。频率分析是设计师日常工作常见的设计验证。 今天给大家分享的是Simulation的频率分析操作方法: …