GO语言网络编程(并发编程)Sync

2023-11-07

GO语言网络编程(并发编程)Sync

1、Sync

1.1.1. sync.WaitGroup

在代码中生硬的使用time.Sleep肯定是不合适的,Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法:

方法名 功能
(wg * WaitGroup) Add(delta int) 计数器+delta
(wg *WaitGroup) Done() 计数器-1
(wg *WaitGroup) Wait() 阻塞直到计数器变为0
sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。

我们利用sync.WaitGroup将上面的代码优化一下:

var wg sync.WaitGroup

func hello() {
    defer wg.Done()
    fmt.Println("Hello Goroutine!")
}
func main() {
    wg.Add(1)
    go hello() // 启动另外一个goroutine去执行hello函数
    fmt.Println("main goroutine done!")
    wg.Wait()
}

需要注意sync.WaitGroup是一个结构体,传递的时候要传递指针。

1.1.2 sync.Once

说在前面的话:这是一个进阶知识点。

在编程的很多场景下我们需要确保某些操作在高并发的场景下只执行一次,例如只加载一次配置文件、只关闭一次通道等。

Go语言中的sync包中提供了一个针对只执行一次场景的解决方案–sync.Once。

sync.Once只有一个Do方法,其签名如下:

func (o *Once) Do(f func()) {}

注意:如果要执行的函数f需要传递参数就需要搭配闭包来使用。

加载配置文件示例

延迟一个开销很大的初始化操作到真正用到它的时候再执行是一个很好的实践。因为预先初始化一个变量(比如在init函数中完成初始化)会增加程序的启动耗时,而且有可能实际执行过程中这个变量没有用上,那么这个初始化操作就不是必须要做的。我们来看一个例子:

var icons map[string]image.Image

func loadIcons() {
    icons = map[string]image.Image{
        "left":  loadIcon("left.png"),
        "up":    loadIcon("up.png"),
        "right": loadIcon("right.png"),
        "down":  loadIcon("down.png"),
    }
}

// Icon 被多个goroutine调用时不是并发安全的
func Icon(name string) image.Image {
    if icons == nil {
        loadIcons()
    }
    return icons[name]
}

多个goroutine并发调用Icon函数时不是并发安全的,现代的编译器和CPU可能会在保证每个goroutine都满足串行一致的基础上自由地重排访问内存的顺序。loadIcons函数可能会被重排为以下结果:

func loadIcons() {
    icons = make(map[string]image.Image)
    icons["left"] = loadIcon("left.png")
    icons["up"] = loadIcon("up.png")
    icons["right"] = loadIcon("right.png")
    icons["down"] = loadIcon("down.png")
}

在这种情况下就会出现即使判断了icons不是nil也不意味着变量初始化完成了。考虑到这种情况,我们能想到的办法就是添加互斥锁,保证初始化icons的时候不会被其他的goroutine操作,但是这样做又会引发性能问题。

使用sync.Once改造的示例代码如下:

var icons map[string]image.Image

var loadIconsOnce sync.Once

func loadIcons() {
    icons = map[string]image.Image{
        "left":  loadIcon("left.png"),
        "up":    loadIcon("up.png"),
        "right": loadIcon("right.png"),
        "down":  loadIcon("down.png"),
    }
}

// Icon 是并发安全的
func Icon(name string) image.Image {
    loadIconsOnce.Do(loadIcons)
    return icons[name]
}

sync.Once其实内部包含一个互斥锁和一个布尔值,互斥锁保证布尔值和数据的安全,而布尔值用来记录初始化是否完成。这样设计就能保证初始化操作的时候是并发安全的并且初始化操作也不会被执行多次。

1.1.3 sync.Map

Go语言中内置的map不是并发安全的。请看下面的示例:

var m = make(map[string]int)

func get(key string) int {
    return m[key]
}

func set(key string, value int) {
    m[key] = value
}

func main() {
    wg := sync.WaitGroup{}
    for i := 0; i < 20; i++ {
        wg.Add(1)
        go func(n int) {
            key := strconv.Itoa(n)
            set(key, n)
            fmt.Printf("k=:%v,v:=%v\n", key, get(key))
            wg.Done()
        }(i)
    }
    wg.Wait()
}

上面的代码开启少量几个goroutine的时候可能没什么问题,当并发多了之后执行上面的代码就会报fatal error: concurrent map writes错误。

像这种场景下就需要为map加锁来保证并发的安全性了,Go语言的sync包中提供了一个开箱即用的并发安全版map–sync.Map。开箱即用表示不用像内置的map一样使用make函数初始化就能直接使用。同时sync.Map内置了诸如Store、Load、LoadOrStore、Delete、Range等操作方法。

var m = sync.Map{}

func main() {
    wg := sync.WaitGroup{}
    for i := 0; i < 20; i++ {
        wg.Add(1)
        go func(n int) {
            key := strconv.Itoa(n)
            m.Store(key, n)
            value, _ := m.Load(key)
            fmt.Printf("k=:%v,v:=%v\n", key, value)
            wg.Done()
        }(i)
    }
    wg.Wait()
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GO语言网络编程(并发编程)Sync 的相关文章

  • 如何修复“缺少表的 FROM 子句条目”错误

    我正在尝试根据游戏 ID 获取平台名称 我有如下三个表 我正在尝试加入它们以获得所需的结果 Games Id 1 2 3 4 Game Platforms Id game id platform id 1 1 1 2 1 2 3 3 3
  • 解压文件的简单方法

    有没有一种简单的方法可以用 Go 解压文件 现在我的代码是 func Unzip src dest string error r err zip OpenReader src if err nil return err defer r Cl
  • 在 Golang 中运行外部 python,捕获连续的 exec.Command Stdout

    所以我的 go 脚本将像这样调用外部 python cmd exec Command python game py cmd Stdout os Stdout cmd Stderr os Stderr go func err cmd Run
  • golang中通道缓冲容量0和1的区别

    我已将通道缓冲区大小设置为零 例如var intChannelZero make chan int 当从intChannelZero将被阻止 直到intChannelZero有价值 另外 我将通道缓冲区大小设置为 1 例如var intCh
  • 实现具有更广泛方法签名的接口

    在Go中 是否有一种方法可以使用方法来实现接口 其中实现中相应方法的返回类型 比 预期返回类型 更宽 这很难解释 所以这里有一个例子 在 Go Playground 中运行以下示例代码时出现此错误 prog go 36 14 cannot
  • Go客户端程序生成大量TIME_WAIT状态的socket

    我有一个 Go 程序 它从多个 goroutine 生成大量 HTTP 请求 运行一段时间后 程序报错 connect cannot allocaterequestedaddress 当检查时netstat 我得到大量 28229 个连接T
  • golang.org 包和标准库之间的区别

    我使用 go 已经有一段时间了 我注意到 Go 标准库 和 golang org x 之间存在重复的包 我的问题是 为什么它们被释放两次 在这两者中 我应该使用哪一个 更新的 规范的等 到目前为止我注意到的一些示例包已发布两次 golang
  • 当使用 k8s.io/client-go 库的 kubernetes 部署发生更改时获得通知的最佳方式是什么?

    Context 我正在编写一个使用k8s io client go https github com kubernetes client go 图书馆 这里是 godocs https godoc org k8s io client go
  • container_memory_working_set_bytes 与 process_resident_memory_bytes 和total_rss 之间的关系

    我希望了解以下关系 容器内存工作集字节 vs 进程驻留内存字节 vs 总计RSS 容器内存 rss 文件映射以便更好地配备OOM可能性警报系统 这似乎违背了我的理解 这让我现在感到困惑 如果容器 pod 运行单个进程 执行用 Go 编写的编
  • “http:多个response.WriteHeader调用”有什么不好的影响?

    尽管我发现 http 多个响应 WriteHeader 调用 例外 但我的服务器表现良好 此异常不会导致我的服务器出现恐慌或行为异常 我进行了很多搜索 但只找到了如何解决这个问题 没有文档描述异常的不良影响 有人可以帮我找出为什么 http
  • 如何使用 go1.6.2 构建 linux 32 位

    有没有任何组合GOARCH and GOOS我可以设置哪些值来构建 ELF 32 位二进制文 件 GOOS linux and GOARCH 386 更多示例 架构 32 bit gt GOARCH 386 64 bit gt GOARCH
  • golang中如何将相对路径解析为绝对路径?

    节点中是否有类似 path resolve 的API 或者有什么东西可以做同样的事情 例如 nodejs代码 path resolve sample sh 应该得到 home currentuser sample sh 解决 表示用户主目录
  • 如何在 Go 中使用与包同名的变量名?

    文件或目录的常见变量名称是 path 不幸的是 这也是 Go 中包的名称 此外 在 DoIt 中更改路径作为参数名称 如何编译此代码 package main import path os func main DoIt file txt f
  • 重新设计循环依赖缺陷

    我有一堆小服务 它们共享一些常见的包 例如Logger Configuration and Net 我在单独的项目中编写了每个包 问题是我的Logger需求包Configuration用于设置 和我的Configuration not仅由L
  • Go的堆接口实现的优先级队列的大小限制

    在Java中 有一个具有大小属性 的PriorityQueue 我在这里也期待同样的事情 如果我没记错的话 用例 一一读取数百万数据并将其发送到优先级队列 我只想要前 5 个计算元素 因此我只想要大小为 5 的堆 优先级队列 我正在尝试使用
  • 如何使用 exec.Command 在 golang 中执行 Mysql 脚本

    您好 我正在尝试执行一个脚本以使用 Golang 将数据填充到数据库中 func executeTestScript cmd exec Command usr local mysql bin mysql h127 0 0 1 P3333 u
  • 不支持的 Perl 语法:`(?<`

    我想解析 cmd gpg list keys 的结果以将其显示在浏览器上 cmd输出是这样的 pub rsa3072 2021 08 03 SC expires 2023 08 03 07C47E284765D5593171C18F00B1
  • 如何覆盖 go 模块中的依赖项?

    In dep您可以选择覆盖依赖项并使其指向不同的存储库 例如以下内容https github com kubermatic glog logrus https github com kubermatic glog logrus库一需要将以下
  • 无法在 Golang 中导入本地模块

    我正在尝试导入本地模块 但无法使用以下命令导入它go mod 我最初使用以下方式构建了我的项目go mod init github com AP Ch2 GOMS 注意我的环境是go1 14我使用 VSCode 作为我的编辑器 这是我的文件
  • 记录 http.ResponseWriter 内容

    Premise 我发现了类似的问题 但不适用于我的情况 因此请不要将其标记为重复 我在 Go 中有一个 HTTP 服务器 并且创建了一个中间件记录请求 响应时间 我也想记录响应 我用过httputil DumpRequest在一个名为的函数

随机推荐

  • mysql 主从 mysqldump_MySQL5.7基于mysqldump的主从复制

    1创建账号 创建用于复制的账号 GRANT REPLICATION SLAVE ON TO repl 192 168 1 IDENTIFIED BY PASSWORD repl4salve 创建用于监控的账号 grant replicati
  • javascript模块化理解(通俗易懂)

    http www ruanyifeng com blog 2012 10 javascript module html http www ruanyifeng com blog 2012 10 asynchronous module def
  • epoll多路复用-----epoll_create1()、epoll_ctl()、epoll_wait()

    include
  • 虚幻引擎程序化资源生成框架PCG 之 常用撒点方法小结

    PCG真好玩 门槛很低 天花板很高 文章目录 前言 1 基本撒点 1 1 Landscape上撒点 1 2 使用射线检测在地表面撒点 1 3 使用曲线撒点 1 3 1 沿曲线撒点 1 3 2 在闭合曲线内部撒点 1 4 在StaticMes
  • 液晶LCD1602使用介绍

    液晶LCD1602简介 LCD1602液晶也叫1602字符型液晶模块 LCD1602液晶是一种专门用来显示字母 数字 符号的点阵型液晶模块 LCD1602液晶是由若干个5x7点阵字符位组成 每个点阵字符位都可以显示一个字符 包括字母 数字
  • Elasticsearch更新语法

    文章目录 update 语法 示例 数值计算 add列表元素 remove列表元素 add字段 remove字段 delete文档 detect noop upsert更新 新增 scripted upsert doc as upsert
  • python读取二进制文件并画图_使用Python读取二进制文件的实例讲解

    目标 目标文件为一个float32型存储的二进制文件 按列优先方式存储 本文使用Python读取该二进制文件并使用matplotlib pyplot相关工具画出图像 工具 Python3 matplotlib os struct numpy
  • 理解深度学习中的学习率

    学习率是最影响性能的超参数之一 如果我们只能调整一个超参数 那么最好的选择就是它 相比于其它超参数学习率以一种更加复杂的方式控制着模型的有效容量 当学习率最优时 模型的有效容量最大 从手动选择学习率到使用预热机制 本文介绍了很多学习率的选择
  • 渲染富文本编辑器并设置富文本编辑器的高度

    目标 富文本编辑器 vue quill editor 的基本使用 vue quill editor npm 一 渲染富文本编辑器 运行如下的命令 在项目中安装富文本编辑器 npm i vue quill editor 3 0 6 S 在项目
  • 关于android指纹识别兼容6.0以下版本

    指纹识别功能以及兼容6 0以下的版本 刚好公司安全问题用到了指纹识别和人脸识别 下面就介绍一下指纹识别的功能 其实看了源码要实现这个挺简单的 其实要使指纹识别功能能兼容6 0以下的版本主要是用FingerprintManagerCompat
  • java自学笔记1:java中的类

    一 1 类的重要性 所有java程序都以class为组织单元 2 什么是类 类是模子 确定对象将会y拥有的特征 属性 和行为 方法 3 类的组成 属性和方法 4 定义一个类的步骤 a 定义类名 b 编写类的属性 c 编写类的方法 publi
  • 如何在html中加入视频

    第一步 首先下载video js 百度一下就能找到 这个是下载后的目录 第二步 先把要用到的js css swf都加载到html页面上 如 第三步 加入下面的代码
  • 源根‘E:\XXX’在模块‘项目名’中重复

    解决办法 新建一个文件夹存储项目 然后打开新建好的项目 就没有报错了
  • stem课程方案

    现在人们开始关注孩子的STEM教育 为了推动这一新的教育发展势头 父母可以在激发孩子关于STEM教育及其专业可能性方面发挥重要作用 在孩子的stem教育上 父母究竟应该怎么做呢 格物斯坦小坦克来告诉你 发现孩子天生的亲和力和学习能力 每个孩
  • Unicode转Utf-8. C++ 中文字符串比较

    c 中如何比较中文字符串 假如通过TCP UDP从其他地方传过来一个UTF 8编码的中文字符串 现在需要你判断这个字符串是否是你想要的 之前的尝试 std string recv data std string my string 字符串
  • 关于数据生成二维码保存和解密删除二维码

    文章目录 前言 一 pom配置依赖 二 文件引入 1 BufferedImageLuminanceSource 2 QRCodeUtil 3 MyPicConfig 4 UploadUtils 三 测试 前言 所需文件 MyPicConfi
  • 荣耀总裁赵明:年轻人拖着世界往前走

    有限的资源聚焦一个领域 才能在纵深上发展 一点的突破可以带动一个行业的发展 赵明在演讲中提到 自认资源有限的荣耀 找准了两个基点 一个是年轻人 一个是科技 两者间相互映照 年轻人更易于接受 也更热爱科技 科技在年轻人身上会出现更强的化学反应
  • 卡诺图简单逻辑化简与五变量卡诺图化简

    一 格雷码编码规则 画卡诺图的时候需要先将所有变量可能以格雷码的形式排列在方格两侧 所有变量有2 n个 虽然我们常用的变量为四个及以下 可以熟记格雷码 但为了学习还是有必要了解格雷码的编码规则 格雷码的基本特点就是任意两个相邻的代码只有一位
  • 什么?强化学习竟然来源于心理学?

    欢迎大家前往腾讯云 社区 获取更多腾讯海量技术实践干货哦 本文由罗晖发表于云 社区专栏 1 Google的DQN论文 2015年2月 Google在Nature上发表了一篇论文 见附件 Human level control through
  • GO语言网络编程(并发编程)Sync

    GO语言网络编程 并发编程 Sync 1 Sync 1 1 1 sync WaitGroup 在代码中生硬的使用time Sleep肯定是不合适的 Go语言中可以使用sync WaitGroup来实现并发任务的同步 sync WaitGro