【Go】原子并发操作

news2025/3/17 4:51:14

        

目录

一、基本概念

支持的数据类型

主要函数

使用场景

二、基础代码实例

开协程给原子变量做加法

统计多个变量

原子标志判断

三、并发日志记录器

四、并发计数器与性能监控

五、优雅的停止并发任务

worker函数

Main函数

应用价值


        Go语言中,原子并发操作是非常常用的,确保协程环境中对资源的共访是安全的。Go的sync/atomic包提供了一系列底层的原子性操作函数,允许你在基本数据类型上执行无锁的线程安全操作。使用原子操作可以避免在并发访问时使用互斥锁(mutexes),从而在某些情况下提高性能。

一、基本概念

        原子操作可以保证在多线程环境中,单个操作是不可中断的,即在完成之前不会被线程切换影响。这是通过硬件级别的支持实现的,确保了操作的原子性。

支持的数据类型

Go的sync/atomic包支持几种基本数据类型的原子操作:

  • int32, int64
  • uint32, uint64
  • uintptr
  • Pointer(对于任意类型的原子指针操作)

主要函数

  • AddInt32, AddInt64, AddUint32, AddUint64: 原子加法操作。
  • LoadInt32, LoadInt64, LoadUint32, LoadUint64: 原子加载操作,用于读取一个值。
  • StoreInt32, StoreInt64, StoreUint32, StoreUint64: 原子存储操作,用于写入一个值。
  • SwapInt32, SwapInt64, SwapUint32, SwapUint64: 原子交换操作,写入新值并返回旧值。
  • CompareAndSwapInt32, CompareAndSwapInt64, CompareAndSwapUint32, CompareAndSwapUint64: 比较并交换操作,如果当前值等于旧值,则写入新值。

使用场景

原子操作通常用在性能敏感且要求高并发的场景,例如:

  • 实时计数器
  • 状态标志
  • 无锁数据结构的实现

        原子操作提供了一种比锁更轻量级的并发控制方法,尤其适用于操作简单且频繁的场景。不过,原子操作的使用需要更谨慎,以避免复杂逻辑中可能的逻辑错误。在设计并发控制策略时,适当的选择使用锁还是原子操作,可以帮助你更好地平衡性能和开发效率。

二、基础代码实例

开协程给原子变量做加法

package main

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

func main() {

	// We'll use an atomic integer type to represent our
	// (always-positive) counter.
	var ops atomic.Uint64

	// A WaitGroup will help us wait for all goroutines
	// to finish their work.
	var wg sync.WaitGroup

	// We'll start 50 goroutines that each increment the
	// counter exactly 1000 times.
	for i := 0; i < 500; i++ {
		wg.Add(1)

		go func() {
			for c := 0; c < 10000; c++ {

				// To atomically increment the counter we use `Add`.
				ops.Add(1)
			}

			wg.Done()
		}()
	}

	// Wait until all the goroutines are done.
	wg.Wait()

	// Here no goroutines are writing to 'ops', but using
	// `Load` it's safe to atomically read a value even while
	// other goroutines are (atomically) updating it.
	fmt.Println("ops:", ops.Load())
}

统计多个变量

        我们可以使用多个原子变量来跟踪不同类型的操作。

package main

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

func main() {
	var adds atomic.Uint64
	var subs atomic.Uint64
	var wg sync.WaitGroup

	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func() {
			for c := 0; c < 1000; c++ {
				adds.Add(1)
			}
			wg.Done()
		}()

		wg.Add(1)
		go func() {
			for c := 0; c < 1000; c++ {
				subs.Add(1)
			}
			wg.Done()
		}()
	}

	wg.Wait()
	fmt.Println("Adds:", adds.Load(), "Subs:", subs.Load())
}

原子标志判断

        使用原子变量作为一个简单的标志来控制是否所有协程都应该停止工作。

package main

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

func main() {
	var ops atomic.Uint64
	var stopFlag atomic.Bool
	var wg sync.WaitGroup

	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func() {
			for !stopFlag.Load() {
				ops.Add(1)
				time.Sleep(10 * time.Millisecond) // 减缓增加速度
			}
			wg.Done()
		}()
	}

	time.Sleep(500 * time.Millisecond) // 运行一段时间后
	stopFlag.Store(true)               // 设置停止标志
	wg.Wait()
	fmt.Println("ops:", ops.Load())
}

三、并发日志记录器

        在一个多协程环境中,我们可能需要记录日志,但又希望避免因为并发写入而导致的问题。我们可以使用sync.Mutex来确保日志写入的原子性。

package main

import (
	"fmt"
	"log"
	"os"
	"sync"
)

var (
	logger   *log.Logger
	logMutex sync.Mutex
)

func main() {
	file, err := os.OpenFile("log.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)

	if err != nil {
		log.Fatalln("Failed to open log file")

	}
	defer file.Close()
	logger = log.New(file, "", log.Ldate|log.Ltime|log.Lshortfile)
	var wg sync.WaitGroup

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func(id int) {
			logMutex.Lock()
			logger.Println("Goroutine %d is runing...", id)
			logMutex.Unlock()
			wg.Done()
		}(i)
	}
	wg.Wait()
	fmt.Println("All goroutines finished")
}

四、并发计数器与性能监控

        在一个网络服务器或数据库中,我们可能需要监控并发请求的数量或特定资源的使用情况,使用原子操作可以无锁地实现这一点。

package main

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

var requestCount atomic.Int64

func handler(w http.ResponseWriter, r *http.Request) {
	requestCount.Add(1)
	fmt.Fprintf(w, "Hello, visitor number %d!", requestCount.Load())
}

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}

 

 

五、优雅的停止并发任务

       在处理诸如网络服务或后台任务处理器的程序时,我们可能需要在收到停止信号后优雅地中断并发任务。

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

func worker(ctx context.Context, id int, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("Worker %d stopping\n", id)
			return
		default:
			fmt.Printf("Worker %d is working\n", id)
			time.Sleep(time.Second)
		}
	}
}

func main() {
	var wg sync.WaitGroup
	ctx, cancel := context.WithCancel(context.Background())

	for i := 1; i <= 5; i++ {
		wg.Add(1)
		go worker(ctx, i, &wg)
	}

	time.Sleep(5 * time.Second)
	cancel() // 发送取消信号
	wg.Wait()
	fmt.Println("All workers stopped.")
}

worker函数

func worker(ctx context.Context, id int, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			fmt.Printf("Worker %d stopping\n", id)
			return
		default:
			fmt.Printf("Worker %d is working\n", id)
			time.Sleep(time.Second)
		}
	}
}
  • worker是一个协程函数,接受一个context.Context对象、一个整数id作为工人标识,和一个sync.WaitGroup来同步协程。
  • defer wg.Done(): 确保在函数返回时调用wg.Done(),表明该协程的工作已完成,这对于等待组来维护协程计数非常重要。
  • select语句用于处理多个通道操作。在这里,它监听ctx.Done()通道,这是context提供的方式,用于接收上下文取消事件。
    • ctx.Done()通道接收到信号时(这发生在主协程调用cancel()函数时),输出停止信息,并通过return退出无限循环,结束协程执行。
    • default分支在没有信号时执行,模拟工作负载并通过time.Sleep(time.Second)模拟一秒钟的工作时间。

Main函数

  • ctx, cancel := context.WithCancel(context.Background()): 创建一个可取消的上下文ctx,和一个cancel函数,用于发送取消信号。
  • 循环启动5个工作协程,每个通过go worker(ctx, i, &wg)启动,并传递上下文、ID和等待组。
  • time.Sleep(5 * time.Second): 主协程等待5秒钟,给工作协程一定时间执行。
  • cancel(): 调用取消函数,这会向ctx.Done()发送信号,导致所有监听该通道的工作协程接收到取消事件并停止执行。
  • wg.Wait(): 阻塞直到所有工作协程调用wg.Done(),表明它们已经停止。
  • 输出“All workers stopped.”表示所有工作协程已经优雅地停止。

应用价值

        这种模式的使用在需要对多个并发执行的任务进行优雅中断和资源管理时非常有用,例如:

  • 处理HTTP请求时,在请求超时或取消时停止后台处理。
  • 控制长时间运行或复杂的后台任务,允许随时取消以释放资源。
  • 在微服务架构中,通过控制信号来优雅地关闭服务或组件。

        这种模式提高了程序的健壮性和响应性,使得程序能够在控制下安全地处理并发操作,同时减少资源的浪费。

总结

        Go语言中的原子操作是通过sync/atomic包提供的,用于实现低级的同步原语。原子操作可以在多协程环境中安全地操作数据,而不需要加锁,因此在某些场景下比使用互斥锁(mutex)具有更好的性能。下面是关于Go中原子操作及其相关内容的详细总结:

1. 原子操作的基本概念

        原子操作指的是在多线程或多协程环境中,能够保证中间状态不被其他线程观察到的操作。这些操作在执行的过程中不会被线程调度器中断,Go语言通过硬件支持保证了这些操作的原子性。

2. 原子类型支持

sync/atomic包支持以下基本数据类型的原子操作:

  • 整型(int32, int64
  • 无符号整型(uint32, uint64
  • 指针类型(uintptr
  • 更通用的指针操作(unsafe.Pointer

3. 主要原子操作

  • 加载(Load): 原子地读取变量的值。
  • 存储(Store): 原子地写入新值到变量。
  • 增加(Add): 原子地增加变量的值。
  • 交换(Swap): 原子地将变量设置为新值,并返回旧值。
  • 比较并交换(Compare And Swap, CAS): 如果当前变量的值等于旧值,则将变量设置为新值,并返回操作是否成功。

4. 原子操作的用途

        原子操作通常用于实现无锁的数据结构和算法,以及在不适合使用互斥锁的高并发场景中保护共享资源。它们特别适用于管理共享状态、实现简单的计数器或标志、以及在状态机中进行状态转换。

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

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

相关文章

ABAP MESSAGE 常用的类型

类型文本描述A终止处理终止&#xff0c;用户必须重启事务X退出与消息类型A 类似&#xff0c;但带有程序崩溃 MESSAGE_TYPE_XE错误处理受到干扰&#xff0c;用户必须修正输入条目,左下角提示!W警告处理受到干扰&#xff0c;用户可以修正输入条目,左下角提示!I信息处理受到干扰&a…

中断的设备树修改及上机实验(按键驱动)流程

写在前面的话&#xff1a;对于 GPIO 按键&#xff0c;我们并不需要去写驱动程序&#xff0c;使用内核自带的驱动程序 drivers/input/keyboard/gpio_keys.c 就可以&#xff0c;然后你需要做的只是修改设备树指定引脚及键值。 根据驱动文件中的platform_driver中的.of_match_tabl…

一套3种风格经典的wordpress免费主题模板

wordpress免费企业主题 https://www.wpniu.com/themes/39.html 免费wordpress企业模板 https://www.wpniu.com/themes/43.html 免费wordpress企业主题 https://www.wpniu.com/themes/44.html

图灵奖2023:Avi Wigderson的开创性贡献揭示计算中的随机性和伪随机性

文章目录 每日一句正能量前言背景什么是理论计算机科学&#xff1f;为什么随机性很重要&#xff1f;三篇影响深远的论文Avi Wigderson在计算复杂性理论方面的贡献及其对现代计算的影响Avi Wigderson对随机性和伪随机性在计算中作用的理解及其实际应用Avi Wigderson的学术生涯和…

超越GPT-4V!马斯克发布Grok-1.5 With Vision

在 Grok-1 开源后不到一个月&#xff0c;xAI 的首个多模态模型就问世了。Grok-1.5V是XAI的第一代多模态模型&#xff0c;除了其强大的文本处理能力之外&#xff0c;Grok现在还能够处理包括文档、图表、图形、屏幕截图和照片在内的各种视觉信息。相信Grok-1.5V将很快提供给现有的…

基于unity+c#的随机点名系统(简单UI界面+列表+数组)

目录 一、功能界面显示 二、UI 1、视频的使用 &#xff08;1&#xff09;渲染纹理 &#xff08;2&#xff09; 视频铺全屏 &#xff08;3&#xff09;视频的调用 2、 下拉文本框的使用&#xff08;旧版&#xff09; 3、输入文本框的使用&#xff08;旧版&#xff09; …

OneFlow深度学习简介

介绍 OneFlow是一个基于深度学习的开源框架,主要面向机器学习工程师和研究人员。它提供了类似于其他深度学习框架(如TensorFlow和PyTorch)的API,同时具有高性能和高效的特点。OneFlow专注于在大规模数据集和分布式环境下的训练和推理,以及在生产环境中的部署和优化。其设计…

冯诺依曼结构理解

冯诺依曼结构 存储器&#xff1a;内存 数据是要在计算机的体系结构中进行流动的&#xff0c;在流动过程中对数据加工处理 从一个设备到另一个设备&#xff0c;本质是一种拷贝 CPU的计算速度是很快的&#xff0c;所以数据设备间的拷贝效率&#xff0c;决定了计算机整体的基本效率…

vue3第十八节(diff算法)

引言&#xff1a; 上一节说了key的用途&#xff0c;而这个key属性&#xff0c;在vue的vnode 中至关重要&#xff0c;直接影响了虚拟DOM的更新机制&#xff1b; 什么场景中会用到diff算法 如&#xff1a;修改响应式属性需要重新渲染页面&#xff0c;会重新执行render渲染函数返…

Maven 项目 JDK 8、JDK 17 多版本 Java 编译依赖最佳实践

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

layui复选框勾选取消勾选事件监听

监听事件放置位置&#xff1a; form.on(checkbox(equipInputClick), function(data){var a data.elem.checked;var val data.value;if(a true){}else{}});html部分 <input lay-filter"equipInputClick" type"checkbox" lay-skin"primary&quo…

ViT——nlp和cv进行了统一,使多模态成为可能

题目:AN IMAGE IS WORTH 16X16 WORDS: TRANSFORMERS FOR IMAGE RECOGNITION AT SCALE 1.概述之前的transformer在cv中应用,大部分是将CNN模型中部分替换成transformer block(整体网络结构不变)或者用transformer将不同网络连接起来,而本文提出:一个针对图像patch的纯的t…

LeetCode-706. 设计哈希映射【设计 数组 哈希表 链表 哈希函数】

LeetCode-706. 设计哈希映射【设计 数组 哈希表 链表 哈希函数】 题目描述&#xff1a;解题思路一&#xff1a;超大数组解题思路二&#xff1a;拉链法解题思路三&#xff1a; 题目描述&#xff1a; 不使用任何内建的哈希表库设计一个哈希映射&#xff08;HashMap&#xff09;。…

LeetCode 热题 100 Day03

普通数组 常见的题型有&#xff1a; 取模、区间合并、最大子序列和、最长非0子序列等。 一些解题思路很巧妙&#xff0c;多练多总结。 Leetcode 53. 最大子数组和 [dp动态查找最大值] 题目理解&#xff1a; 给定一个整数数组, 求一个连续的子序列 该子序列满足和最大 要求返回最…

有依赖的的动态规划问题

题目 题型分析 这是比较典型的动态规划的问题。动态规划是什么呢&#xff1f;本质上动态规划是对递归的优化。例如&#xff0c;兔子数列&#xff1a;f(x) f(x - 1) f(x -2), 我们知道 f 代表了计算公式&#xff0c;这里解放思想一下&#xff0c;如果 f 替换为数组&#xff0…

用于 SQLite 的异步 I/O 模块(二十四)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLite的PRAGMA 声明&#xff08;二十三&#xff09; 下一篇&#xff1a;SQLite、MySQL 和 PostgreSQL 数据库速度比较&#xff08;本文阐述时间很早比较&#xff0c;不具有最新参考性&#xff09;&#xff08;二…

【数据结构|C语言版】单链表应用

前言1. 基于单链表实现通讯录1.1 知识要求1.2 功能要求 2. 代码总结2.1 SeqList.h2.2 SeqList.c2.3 Contact.h2.4 Contact.c2.5 test.c 后言 上期回顾&#xff1a;【数据结构|C语言版】单链表 前言 各位小伙伴大家好&#xff01;上期小编讲解了单链表相关知识&#xff0c;在此…

linux 设置定时任务---学习

1、设置定时任务 crontab -e 设置格式参考&#xff1a;【Linux】Linux crontab 命令定时任务设置_crontab 设置每天10:30执行-CSDN博客 测试过程&#xff1a; */1 * * * * /root/cronjob.sh 脚本内容: echo "hell0 cronjob" >> /root/test/hello.txt 实现…

腾讯云服务器CVM标准型S8实例CPU内存、网络和存储性能测评

腾讯云第八代云服务器标准型S8实例基于全新优化虚拟化平台&#xff0c;CPU采用Intel Emerald Rapids 全新处理器&#xff0c;睿频3.0GHz&#xff0c;内存采用最新DDR5&#xff0c;默认网络优化&#xff0c;最高内网收发能力达4500万pps&#xff0c;最高内网带宽可支持120Gbps。…

游戏生成式 AI:编织梦想,避开阴影

想象一下&#xff0c;一个沉浸式的游戏世界中玩家遇到的每个 NPC 都由 AI 驱动&#xff0c;他们能与玩家进行互动&#xff0c;从改变游戏体验。据 Inword 一项研究显示&#xff0c;绝大多数游戏玩家渴望这种互动&#xff0c;愿意投入更多的时间和金钱来玩这种由 AI 驱动的游戏。…