go 语言学习之 mutex

news/2025/1/11 19:32:27/文章来源:https://www.cnblogs.com/timevalue/p/18666127

竞争条件和数据竞争是并发编程中常见的两个概念,它们都涉及到多个线程或进程对共享数据的并发访问,但具体定义和应用场景有所不同。

竞争条件(Race Condition)

定义:竞争条件是指多个线程或进程在读写一个共享数据时,其最终结果依赖于它们执行的相对时间的情形。也就是说,当事件的时序影响一段代码的正确性时,就会发生竞争条件。

示例:假设两个进程P1和P2共享变量a。在某一执行时刻,P1更新a为1,在另一时刻,P2更新a为2。因此两个任务竞争地写变量a,最终变量a的值取决于哪个进程最后执行写操作。

解决方法

  1. 互斥访问:通过使用互斥锁或信号量等机制,确保在任意时刻只有一个线程可以访问共享资源。
  2. 同步访问:通过使用条件变量或事件等机制,确保线程之间的协调和通信。
  3. 数据副本:对于只读的共享资源,可以为每个线程创建一个副本,使每个线程都有自己的私有数据,从而避免竞争条件。
  4. 原子操作:使用原子操作来执行对共享资源的读取和写入操作,这样就能保证这些操作是不可中断的,从而避免竞争条件。
  5. 使用线程安全的数据结构:选择使用线程安全的数据结构,这些数据结构在内部已经实现了对共享资源的同步和互斥访问。
  6. 避免共享资源:尽可能地避免多个线程同时访问和修改共享资源的情况,可以通过设计合理的程序结构和减少共享资源的使用来避免竞争条件。

数据竞争(Data Race)

定义:数据竞争是指多个线程或进程同时访问共享数据,其中至少一个线程或进程在进行写操作,而这些访问没有进行适当的同步。数据竞争的结果通常是未定义的行为,可能导致程序崩溃或输出错误的结果。

特征

  • 并发访问:多个线程或进程同时访问共享数据。
  • 不一致的状态:数据在不同线程中可能处于不同的状态。
  • 不可预测的结果:程序的行为可能因为数据竞争而不可预测。

示例:考虑一个银行转账的场景,两个线程同时对同一个账户进行读写操作,但没有适当的同步机制,导致最终账户余额可能不一致。

解决方法

  1. 使用同步机制:通过使用互斥锁(Mutex)、读写锁(Read/Write Lock)等同步工具来保护共享数据。
  2. 设计良好的并发模型:使用高层次的并发库,以避免手动管理锁和同步,降低数据竞争的风险。
  3. 定期进行代码审查:通过定期的代码审查和测试,识别并修复潜在的并发问题。确保代码遵循最佳实践,并且使用了适当的同步机制。
  4. 使用原子操作:使用原子操作来执行对共享资源的读取和写入操作,这样就能保证这些操作是不可中断的,从而避免数据竞争。

区别

  • 定义:竞争条件是指多个线程或进程在读写共享数据时,其最终结果依赖于它们执行的相对时间的情形。数据竞争是指多个线程或进程同时访问共享数据,其中至少一个线程或进程在进行写操作,而这些访问没有进行适当的同步。
  • 影响:竞争条件可能导致程序行为不确定,但不一定导致未定义行为。数据竞争通常会导致未定义行为,可能导致程序崩溃或数据损坏。
  • 检测:数据竞争可以通过 -race 来检测。竞争条件的检测通常需要更复杂的时序分析和测试。

通过理解这两个概念的区别,可以更好地设计和实现并发程序,避免潜在的错误和性能问题。

原子操作
`//start
package main

import (
"fmt"
"sync"
"sync/atomic"
)

type Account struct {
balance int64
}

func (a *Account) Deposit(amount int64) {
atomic.AddInt64(&a.balance, amount)
}

func (a *Account) Withdraw(amount int64) bool {
for {
currentBalance := atomic.LoadInt64(&a.balance)
if currentBalance < amount {
return false
}
newBalance := currentBalance - amount
// 使用 CompareAndSwapInt64 确保原子性更新
if atomic.CompareAndSwapInt64(&a.balance, currentBalance, newBalance) {
return true
}
// 如果 CAS 失败,说明有其他 goroutine 修改了余额,重新尝试
}
}

func (a *Account) Balance() int64 {
return atomic.LoadInt64(&a.balance)
}

func transfer(from, to *Account, amount int64, wg *sync.WaitGroup) {
defer wg.Done()
if from.Withdraw(amount) {
to.Deposit(amount)
fmt.Println("Transfer successful")
} else {
fmt.Println("Insufficient funds")
}
}

func main() {
account1 := &Account{balance: 10000}
account2 := &Account{balance: 5800}
var wg sync.WaitGroup

// 模拟多个并发转账
for i := 0; i < 98; i++ {wg.Add(1)go transfer(account1, account2, 100, &wg)
}
wg.Wait()fmt.Println("Final balance of account1:", account1.Balance())
fmt.Println("Final balance of account2:", account2.Balance())

}`

使用mutex
`package main

import (
"fmt"
"sync"
)

type Account struct {
balance int64
mu sync.Mutex
}

func (a *Account) Deposit(amount int64) {
a.mu.Lock()
defer a.mu.Unlock()
a.balance += amount
}

func (a *Account) Withdraw(amount int64) bool {
a.mu.Lock()
defer a.mu.Unlock()
if a.balance < amount {
return false
}
a.balance -= amount
return true
}

func (a *Account) Balance() int64 {
a.mu.Lock()
defer a.mu.Unlock()
return a.balance
}

func transfer(from, to *Account, amount int64, wg *sync.WaitGroup) {
defer wg.Done()
if from.Withdraw(amount) {
to.Deposit(amount)
fmt.Println("Transfer successful")
} else {
fmt.Println("Insufficient funds")
}
}

func main() {
account1 := &Account{balance: 10000}
account2 := &Account{balance: 5800}
var wg sync.WaitGroup

// 模拟多个并发转账
for i := 0; i < 98; i++ {wg.Add(1)go transfer(account1, account2, 100, &wg)
}
wg.Wait()fmt.Println("Final balance of account1:", account1.Balance())
fmt.Println("Final balance of account2:", account2.Balance())

}`

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1.11鲜花

感觉这两天停课有点不太对劲…… 本身因为 csp-s 打烂了赛季报销之后就没有什么事了的,车人却还要以 T/P 营赛前要拉进度的理由把我们喊过来停课。 说实话本身还是想停课的,但停了了一天就发现 T 和 P 都去不了,最后车人拉进度又拉的奇快无比(例如放一个一上午的题单有一堆…

电商行业人才加速跑:实战导向型企业员工培训速成策略

随着电商行业的迅猛发展,市场竞争日益激烈,企业对员工的培训需求也愈发迫切。如何在快节奏的市场环境中,快速提升员工的专业技能和服务水平,成为电商企业面临的重大课题。本文将为您介绍一套企业员工培训的速成法,旨在帮助电商企业在竞争中保持领先地位和在培训资源整合中…

教育机构知识管理新纪元:构建高效知识中台实战策略

在当今这个信息爆炸的时代,教育机构面临着前所未有的挑战与机遇。如何高效地整合、管理和传播知识,成为了决定教育机构竞争力的关键因素之一。知识中台作为连接前台业务与后台资源的桥梁,正逐渐成为众多教育机构转型升级的重要抓手。本文将为您提供一份详尽的知识中台搭建指…

23. 对话框控件

一、对话框应用对话框窗口是一个用来完成简单任务或者和用户进行临时交互的顶层窗口,通常用于输入信息、确认信息或者提示信息。QDialog 类是所有对话框窗口类的基类。按照运行时是否可以和其他窗口进行交互操作,对话框分为模式(或模态)对话框和非模式对话框。对于带有模式…

全栈开发之小程序 网快速笔记,复习springboot 假期复习一套课程

第六章登陆与注册 本章主要内容如下登陆注册相关<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper n…

施耐德 三菱 西门子PLC 以太网口S7-1200/1500系列通讯协议解析说明文档

资料参考链接: https://item.taobao.com/item.htm?abbucket=1&id=766532329733&ns=1&pisk=g0VseN0PDhxsC5j0KlbEVm4PVjhjhw5yfEgYrrdwkfhtDJaucc7cIfyIckEIXC7GIxnbjfH0QmoZcja0VwSPa_zgSjcR4g5yCEod7bAxWqKqv23rGVC1T97TSjcA4eRAU_ag2s7Opd3xJwgqlFnYDAhpvVoKWd…

使用html2canvas生成图片时遇到文字重叠问题

在使用html2canvas.js插件生成图片时,如果文字中有特殊符合时,生成的图片会出现文字重叠的问题,例如这样。 后面发现是换行时英文特殊标点占位与文字占位不同导致该问题。解决方法:为特殊字符添加【letter-spacing: 1px;】css。 如果把全部文字都加letter-spacing: 1px;会…

BurpSuite之SQL 注入测试实操

1、安装:CO2插件进入【BurpSuite】---【拓展】---【BApp商店】,安装完成后,[已安装]列会有:√ 拦截后发送到【重放器】: 重发器中【请求】中操作:【拓展】---【CO2】---【发送到SQLMapper】 2、SQL注入 (1)操作登录 (2)进行拦截,发送给【重放器】: (3)修改name的…

《痞子衡嵌入式半月刊》 第 116 期

痞子衡嵌入式半月刊: 第 116 期这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻,农历年分二十四节气,希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly),欢迎提交 issue,投稿或推荐你知道的嵌入式那些事儿。 上期回顾 :《…

【DNS攻防】深入探讨DNS数据包注入与DNS中毒攻击检测 (C/C++代码实现)

DNS数据包注入和DNS中毒攻击是网络安全领域中的两个重要主题。DNS(域名系统)是互联网中的一项核心服务,负责将域名转换为与之相对应的IP地址。 DNS数据包注入是指攻击者通过篡改或伪造DNS请求或响应数据包来干扰或破坏DNS服务的过程。攻击者可通过注入恶意数据包来改变DNS解…

代码随想录算法训练营第4天 | 24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II,链表总结篇

一、刷题部分 1.1 24. 两两交换链表中的节点原文链接:代码随想录 题目链接:24. 两两交换链表中的节点 - 力扣(LeetCode)1.1.1 题目描述 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交…

windows7老爷机安装蓝牙适配器遇到的坑

连续买了3个蓝牙接收器,都无法在我的win7电脑上使用,最后询问小二、加上自己上网查询,最终解决。 首先,安装蓝牙驱动,重启电脑。 然后,说明书上说,右下角的系统托盘理应有个蓝牙图标,但是我这里不显示,所以,要另寻它法。 右键点击网络图标,【打开网络和共享中心】看…