Golang 并发机制-2:Golang Goroutine 和竞争条件

news2025/2/2 0:11:53

在今天的软件开发中,我们正在使用并发的概念,它允许一次执行多个任务。在Go编程中,理解Go例程是至关重要的。本文试图详细解释什么是例程,它们有多轻,通过简单地使用“go”关键字创建它们,以及可能出现的竞争条件和共享数据问题等主要同步困难。

Goroutine 介绍

Goroutine 是 Go 编程语言中并发编程的基本构建块。它本质上是一个轻量级的执行线程,在 Go 程序中与其他 Goroutine 并行运行。与其它编程语言中的传统线程不同,Goroutine 由 Go 运行时管理,在内存和 CPU 利用率方面都更高效。

  • 性质与效率

Goroutine的突出特性之一是它们的轻量级特性。传统线程可能是资源密集型的,会消耗大量的内存和CPU资源。相比之下,gooutine非常高效,支持创建数千个这样的例程,而不会造成显著的开销。

Goroutine的效率源于它们能够跨较少数量的OS线程进行多路复用,并根据工作负载动态调整其分配。这意味着Go程序可以有效地利用多个内核和处理器,而不需要大量的手动线程管理。
在这里插入图片描述

Goroutine示例(‘ go ’关键字)

在Go中创建Goroutine非常简单,这要归功于“Go”关键字。当你在函数调用前加上‘ go ’时,go会创建新的gooutine来并发地执行该函数。

import (
    "fmt"
    "time"
)

func sayHello() {
    for i := 0; i < 5; i++ {
        fmt.Println("Hello, World!")
        time.Sleep(time.Millisecond * 500)
    }
}

func main() {
    go sayHello() // Start a new Goroutine
    time.Sleep(time.Second * 2)
    fmt.Println("Main function")
}

在上面的例子中,‘ sayHello ’函数与‘ main ’函数并发执行,使其成为在Go中利用并发性的一种简单而有效的方法。

竞争条件

虽然在并发编程中提供了许多优点,但它们也带来了必须小心管理的同步挑战

什么是竞态条件?

当多个Goroutine(轻量级线程)并发地访问共享数据,并且其中至少有一个需要修改数据时,就会出现竞争条件。竞争条件会导致不可预测的结果,因为执行顺序无法保证。它们可能导致数据损坏、崩溃或错误的程序行为。

竞态条件示例:

package main

import (
    "fmt"
    "sync"
)

var sharedCounter int
var wg sync.WaitGroup

func increment() {
    for i := 0; i < 10000; i++ {
        sharedCounter++
    }
    wg.Done()
}

func main() {
    wg.Add(2)
    go increment()
    go increment()
    wg.Wait()
    fmt.Println("Shared Counter:", sharedCounter)
}

在本例中,两个goroutine在没有同步的情况下并发地增加‘ sharedCounter ’变量。这可能导致竞争条件,其中‘ sharedCounter ’的最终值是不可预测的,并且可能是不正确的。

假设 sharedCounter 的初始值为 0,两个 goroutine 同时读取到 0,然后分别加 1 并写回内存,最终 sharedCounter 的值会是 1 而不是 2。为了避免竞争条件,可以使用同步机制,如互斥锁(sync.Mutex)来保护对共享资源的访问。

规避竞争条件

为了消除Go中的竞争条件,你可以使用同步原语,例如互斥锁(互斥锁的简称)。互斥锁确保一次只有一个程序可以访问代码的关键部分。下面是上一个例子的更新版本,使用互斥锁进行正确的同步:

package main

import (
    "fmt"
    "sync"
)

var sharedCounter int
var wg sync.WaitGroup
var mu sync.Mutex

func increment() {
    for i := 0; i < 10000; i++ {
        mu.Lock()
        sharedCounter++
        mu.Unlock()
    }
    wg.Done()
}

func main() {
    wg.Add(2)
    go increment()
    go increment()
    wg.Wait()
    fmt.Println("Shared Counter:", sharedCounter)
}

在修改后的代码中,我们使用‘ mu ’互斥锁来保护‘ sharedCounter ’被修改的代码的临界区。通过锁定和解锁互斥锁,我们确保一次只有一个Goroutine可以访问和修改‘ sharedCounter ’,从而消除了竞争条件。

理解共享数据问题

Go中的共享数据问题发生在多个线程在没有适当同步的情况下并发访问和操作共享数据时。这些问题主要表现为两种形式:

  1. 数据竞争:当两个或多个例程同时访问共享数据时,就会发生数据竞争,从而导致不可预测的结果。数据竞争可能导致数据损坏或不正确的程序行为。

  2. 死锁:当线程被卡住时,会发生死锁,等待彼此释放资源。这可能导致程序陷入停滞。

为了缓解Go中的共享数据问题,开发人员应该使用适当的同步机制,如互斥锁、通道和其他同步原语。以下是一些最佳做法:

  • 使用互斥锁:使用互斥锁保护共享数据,以确保一次只有一个线程可以访问它。
  • 使用通道:通道为程序提供了一种安全的方式来通信和共享数据。它们通过确保对共享数据的受控访问来防止数据竞争。
  • 避免循环依赖:在创建循环依赖时要小心,因为在循环依赖中,各例程会等待彼此释放资源,从而导致死锁。

总之,在用Go编写并发程序时,管理竞争条件和共享数据问题是至关重要的。通过理解这些问题并实现适当的同步技术,开发人员可以创建健壮可靠的并发应用程序,充分利用Go的并发支持,同时避免与共享数据操作相关的陷阱。

最后总结

总之,Go程序是Go编程语言的一个强大功能,它提供了一种轻量级且高效的方式来实现并发性。通过使用“go”关键字,开发人员可以轻松地创建并发执行任务的goroutine。然而,在用Go构建并发应用程序时,必须意识到同步挑战,比如竞争条件和共享数据问题,并采用适当的技术来解决这些问题。

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

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

相关文章

【4Day创客实践入门教程】Day0 创想启程——课程与项目预览

Day0 创想启程——课程与项目预览 目录 Day0 创想启程——课程与项目预览前言学习内容基本的单片机开发技能简单的焊接技能简单的MicroPython程序 后记 Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟Day4 迈…

洛谷P3372 【模板】线段树 1以及分块

【模板】线段树 1 题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行下面两种操作&#xff1a; 将某区间每一个数加上 k k k。求出某区间每一个数的和。 输入格式 第一行包含两个整数 n , m n, m n,m&#xff0c;分别表示该数列数字的个数和操作的总个数。 …

(动态规划基础 打家劫舍)leetcode 198

已知h2和h1&#xff0c;用已知推出未知 推是求答案&#xff0c;回溯是给答案 这里图片给出dfs暴力&#xff0c;再进行记录答案完成记忆化搜索&#xff0c;再转为dp数组 #include<iostream> #include<vector> #include<algorithm> //nums:2,1,1,2 //dp:2,2,…

Python 梯度下降法(四):Adadelta Optimize

文章目录 Python 梯度下降法&#xff08;四&#xff09;&#xff1a;Adadelta Optimize一、数学原理1.1 介绍1.2 实现流程 二、代码实现2.1 函数代码2.2 总代码 三、优缺点3.1 优点3.2 缺点 四、相关链接 Python 梯度下降法&#xff08;四&#xff09;&#xff1a;Adadelta Opt…

旅行的意义:“诗与远方”和在旅途中找寻真我

原文链接&#xff1a;旅行的意义&#xff1a;“诗与远方”和在旅途中找寻真我 困在格子间&#xff0c;心向远方 清晨&#xff0c;闹钟催促&#xff0c;打工人挣扎起床出门。地铁拥挤&#xff0c;工作繁忙&#xff0c;加班成常态&#xff0c;下班时夜幕已深&#xff0c;满心疲惫…

leetcode——将有序数组转化为二叉搜索树(java)

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 平衡 二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视为正确答…

前端js高级25.1.30

原型&#xff1a;函数的组成结构 通过这个图我们需要知道。 假设我们创建了一个Foo函数。 规则&#xff1a;Function.protoType是函数显示原型。__proto__是隐式对象。 Function、Object、Foo函数的__proto__指向了Function.protoType说明。这三个都依托function函数来创建。…

【后端开发】字节跳动青训营之性能分析工具pprof

性能分析工具pprof 一、测试程序介绍二、pprof工具安装与使用2.1 pprof工具安装2.2 pprof工具使用 资料链接&#xff1a; 项目代码链接实验指南pprof使用指南 一、测试程序介绍 package mainimport ("log""net/http"_ "net/http/pprof" // 自…

云原生(五十二) | DataGrip软件使用

文章目录 DataGrip软件使用 一、DataGrip基本使用 二、软件界面介绍 三、附件文件夹到项目中 四、DataGrip设置 五、SQL执行快捷键 DataGrip软件使用 一、DataGrip基本使用 1. 软件界面介绍 2. 附加文件夹到项目中【重要】 3. DataGrip配置 快捷键使用&#xff1a;C…

FreeRTOS学习 --- 任务调度

开启任务调度器 作用&#xff1a;用于启动任务调度器&#xff0c;任务调度器启动后&#xff0c; FreeRTOS 便会开始进行任务调度 该函数内部实现&#xff0c;如下&#xff1a; 1、创建空闲任务&#xff08;优先级最低&#xff09; 2、如果使能软件定时器&#xff0c;则创建定…

2025年人工智能技术:Prompt与Agent的发展趋势与机遇

文章目录 一、Prompt与Agent的定义与区别(一)定义(二)区别二、2025年Prompt与Agent的应用场景(一)Prompt的应用场景(二)Agent的应用场景三、2025年Prompt与Agent的适合群体(一)Prompt适合的群体(二)Agent适合的群体四、2025年Prompt与Agent的发展机遇(一)Prompt的…

区块链 智能合约安全 | 回滚攻击

视频教程在我主页简介和专栏里 目录&#xff1a; 智能合约安全 回滚攻击 总结 智能合约安全 回滚攻击 回滚攻击的本质是”耍赖” 举一个简单的例子,两个人玩石头剪刀布,输了的给对方10块钱,现在A输了,A说这把不算,重来 放在Solidity中,require()函数会检测其中的条件是否满…

【JavaEE进阶】图书管理系统 - 壹

目录 &#x1f332;序言 &#x1f334;前端代码的引入 &#x1f38b;约定前后端交互接口 &#x1f6a9;接口定义 &#x1f343;后端服务器代码实现 &#x1f6a9;登录接口 &#x1f6a9;图书列表接口 &#x1f384;前端代码实现 &#x1f6a9;登录页面 &#x1f6a9;…

TensorFlow 简单的二分类神经网络的训练和应用流程

展示了一个简单的二分类神经网络的训练和应用流程。主要步骤包括&#xff1a; 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与部署 加载和应用已训练的模型 1. 数据准备与预处理 在本例中&#xff0c;数据准备是通过两个 Numpy 数…

docker安装Redis:docker离线安装Redis、docker在线安装Redis、Redis镜像下载、Redis配置、Redis命令

一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull redis:7.4.0 2、离线包下载 两种方式&#xff1a; 方式一&#xff1a; -&#xff09;在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -&#xff09;导出 # 导出镜像…

Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview

Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview 文章目录 Retrieval-Augmented Generation for Large Language Models: A Survey——(1)Overview1. Introduction&Abstract1. LLM面临的问题2. RAG核心三要素3. RAG taxonomy 2. Overv…

LabVIEW透镜多参数自动检测系统

在现代制造业中&#xff0c;提升产品质量检测的自动化水平是提高生产效率和准确性的关键。本文介绍了一个基于LabVIEW的透镜多参数自动检测系统&#xff0c;该系统能够在单一工位上完成透镜的多项质量参数检测&#xff0c;并实现透镜的自动搬运与分选&#xff0c;极大地提升了检…

什么是Maxscript?为什么要学习Maxscript?

MAXScript是Autodesk 3ds Max的内置脚本语言,它是一种与3dsMax对话并使3dsMax执行某些操作的编程语言。它是一种脚本语言,这意味着您不需要编译代码即可运行。通过使用一系列基于文本的命令而不是使用UI操作,您可以完成许多使用UI操作无法完成的任务。 Maxscript是一种专有…

Redis|前言

文章目录 什么是 Redis&#xff1f;Redis 主流功能与应用 什么是 Redis&#xff1f; Redis&#xff0c;Remote Dictionary Server&#xff08;远程字典服务器&#xff09;。Redis 是完全开源的&#xff0c;使用 ANSIC 语言编写&#xff0c;遵守 BSD 协议&#xff0c;是一个高性…

LeetCode:63. 不同路径 II

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;63. 不同路径 II 给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角&#xff08;即 grid[0][0]…