C# 中的多线程同步:原子变量、原子操作、内存顺序和可见性
C# 中的多线程同步:原子变量、原子操作、内存顺序和可见性
引言
随着现代计算机系统的发展,多核处理器已经变得非常普遍。在这种环境下,多线程编程成为提高应用程序性能的关键技术之一。然而,多线程编程带来了新的挑战,其中之一就是确保数据在并发访问时的一致性和安全性。本文将探讨 C# 中的多线程同步机制,特别是原子变量、原子操作、内存顺序和可见性,并通过代码示例来演示如何使用这些工具来构建健壮的并发程序。
原子操作与原子变量
在多线程环境中,原子操作是指那些不可中断的操作。这意味着一旦开始执行,该操作就会一直执行到完成,期间不会被其他线程中断。C# 中提供了多种工具来实现原子操作,包括 Interlocked
类和 System.Threading.Atomic
类。
Interlocked
类
Interlocked
类提供了多个静态方法来执行原子操作,这些方法可以确保在多线程环境中对整型变量的操作是原子的。例如,Interlocked.Increment
和 Interlocked.Decrement
可以用于安全地增加或减少共享变量的值。
示例代码
using System;
using System.Threading;class Program
{private static int counter = 0;private static CountDownEvent allDone = new CountDownEvent(5); // 五个线程static void Increment(){for (int i = 0; i < 100000; i++){Interlocked.Increment(ref counter);}allDone.Signal(); // 通知检查线程,本线程已完成递增}static void Check(){allDone.Wait(); // 等待所有线程完成int finalValue = counter;Console.WriteLine($"Final counter value: {finalValue}");// 验证最终值是否等于预期int expectedValue = 5 * 100000; // 五个线程,每个线程递增100000次if (finalValue == expectedValue){Console.WriteLine("Counter incremented correctly.");}else{Console.WriteLine("Counter did not increment correctly.");}}static void Main(string[] args){var threads = new Thread[5];for (int i = 0; i < 5; i++){threads[i] = new Thread(new ThreadStart(Increment));threads[i].Start();}var checkThread = new Thread(new ThreadStart(Check));checkThread.Start();foreach (var thread in threads){thread.Join();}checkThread.Join();}
}
System.Threading.Atomic
类
从 C# 8.0 开始,引入了 System.Threading.Atomic
类,该类提供了原子类型的实现,类似于 C++ 中的 std::atomic
。使用 System.Threading.Atomic
类可以更加方便地处理原子操作。
示例代码
using System;
using System.Threading;class Program
{private static int counter = 0;private static CountDownEvent allDone = new CountDownEvent(5); // 五个线程static void Increment(){for (int i = 0; i < 100000; i++){Interlocked.Increment(ref counter);}allDone.Signal(); // 通知检查线程,本线程已完成递增}static void Check(){allDone.Wait(); // 等待所有线程完成int finalValue = counter;Console.WriteLine($"Final counter value: {finalValue}");// 验证最终值是否等于预期int expectedValue = 5 * 100000; // 五个线程,每个线程递增100000次if (finalValue == expectedValue){Console.WriteLine("Counter incremented correctly.");}else{Console.WriteLine("Counter did not increment correctly.");}}static void Main(string[] args){var threads = new Thread[5];for (int i = 0; i < 5; i++){threads[i] = new Thread(new ThreadStart(Increment));threads[i].Start();}var checkThread = new Thread(new ThreadStart(Check));checkThread.Start();foreach (var thread in threads){thread.Join();}checkThread.Join();}
}
内存顺序和可见性
在多线程环境中,内存顺序和可见性是非常重要的概念。内存顺序指的是内存操作的顺序,而可见性则确保一个线程对共享数据的修改对其他线程可见。
内存顺序
内存顺序决定了内存操作的执行顺序,这对于确保数据的一致性至关重要。在 C# 中,Interlocked
类提供了不同的内存顺序选项,如 MemoryOrderRelease
、MemoryOrderAcquire
、MemoryOrderSeqCst
等。
示例代码
using System;
using System.Threading;class Program
{private static int flag = 0;private static int counter = 0;static void Writer(){Interlocked.Exchange(ref flag, 1, MemoryOrder.Release); // 设置 flagInterlocked.Exchange(ref counter, 42, MemoryOrder.Release); // 设置 counter}static void Reader(){while (Interlocked.CompareExchange(ref flag, 0, 1, MemoryOrder.Acquire) != 1){Thread.Yield(); // 使当前线程放弃执行权}Console.WriteLine("Counter value: " + Interlocked.Exchange(ref counter, 0, MemoryOrder.Acquire));}static void Main(string[] args){var writerThread = new Thread(Writer);var readerThread = new Thread(Reader);writerThread.Start();readerThread.Start();writerThread.Join();readerThread.Join();Console.WriteLine("Final counter value: " + counter);}
}
内存可见性
内存可见性确保一个线程对共享数据的修改对其他线程可见。在 C# 中,使用 volatile
关键字可以标记一个变量,确保编译器不会对该变量进行优化,从而保证在多线程环境中的内存可见性。但是,volatile
本身并不提供原子性,仅保证内存可见性。
示例代码
using System;
using System.Threading;class Program
{private static volatile bool flag = false;private static int counter = 0;static void Writer(){flag = true;counter = 42;}static void Reader(){while (!flag){Thread.Yield(); // 使当前线程放弃执行权}Console.WriteLine("Counter value: " + counter);}static void Main(string[] args){var writerThread = new Thread(Writer);var readerThread = new Thread(Reader);writerThread.Start();readerThread.Start();writerThread.Join();readerThread.Join();Console.WriteLine("Final counter value: " + counter);}
}
结论
多线程编程需要仔细考虑数据的一致性和同步问题。C# 提供了多种工具来帮助开发者构建健壮的并发程序,包括 Interlocked
类、System.Threading.Atomic
类以及 volatile
关键字。通过合理使用这些工具,可以有效地避免数据竞争和其他并发问题,确保程序的正确性和高效性。
通过上述示例和解释,我们看到了如何在 C# 中使用原子变量、原子操作、内存顺序和可见性来构建可靠的多线程应用程序。希望这篇文章能帮助你在开发并发程序时更好地理解和运用这些概念。
相关文章:
C# 中的多线程同步:原子变量、原子操作、内存顺序和可见性
C# 中的多线程同步:原子变量、原子操作、内存顺序和可见性 引言 随着现代计算机系统的发展,多核处理器已经变得非常普遍。在这种环境下,多线程编程成为提高应用程序性能的关键技术之一。然而,多线程编程带来了新的挑战ÿ…...

视图(mysql)
一、什么是视图 视图是⼀个虚拟的表,它是基于⼀个或多个基本表或其他视图的查询结果集。视图本⾝不存储数 据,⽽是通过执⾏查询来动态⽣成数据。⽤⼾可以像操作普通表⼀样使⽤视图进⾏查询、更新和管 理。视图本⾝并不占⽤物理存储空间,它仅…...

elementui组件el-upload实现批量文件上传
el-upload组件上传文件时,每传一个文件会调一次接口,所以当上传多个文件的时候,有 n 个文件就要调 n 次接口。 刚好之前工作中遇到使用el-upload组件批量上传文件的需求,来看看怎么实现。 思路: 1.取消组件的自动上…...

【JAVA入门】Day45 - 压缩流 / 解压缩流
【JAVA入门】Day45 - 压缩流 / 解压缩流 文章目录 【JAVA入门】Day45 - 压缩流 / 解压缩流一、解压缩流二、压缩流 在文件传输过程中,文件体积比较大,传输较慢,因此我们发明了一种方法,把文件里的数据压缩到一种压缩文件中&#x…...

Qt_自定义信号
目录 1、自定义信号的规定 2、创建自定义信号 3、带参数的信号与槽 4、一个信号连接多个槽 5、信号与槽的断开 结语 前言: 虽然Qt已经内置了大量的信号,并且这些信号能够满足大部分的开发场景,但是Qt仍然允许开发者自定义信号&#…...

【运维方案】某系统运维需求方案参考(doc全原件2024)
系统运维需求方案 1服务目标 2服务人力需求、服务资源需求 3信息资产统计服务需求 4业务应用软件服务需求 5网络、安全系统运维服务需求 6主机、存储系统运维服务需求 7数据库系统运维服务需求 8终端运维服务需求 9综合布线系统服务需求 10大屏幕显示系统的维护需求 11视频会议…...

Linux环境使用Git同步教程
📖 前言:由于CentOS 7已于2024年06月30日停止维护,为了避免操作系统停止维护带来的影响,我们将把系统更换为Ubuntu并迁移数据,在此之前简要的学习Git的上传下载操作。 目录 🕒 1. 连接🕘 1.1 配…...
c++临时对象导致的生命周期问题
对象的生命周期是c中非常重要的概念,它直接决定了你的程序是否正确以及是否存在安全问题。 今天要说的临时变量导致的生命周期问题是非常常见的,很多时候没有一定经验甚至没法识别出来。光是我自己写、review、回答别人的问题就犯了或者看到了许许多多这…...
CSP-J 算法基础 深度优先搜索
文章目录 前言深度优先搜索通俗解释例子深度优先搜索的步骤DFS 的特点生活中的类比 为什么递归问题会变成深度优先搜索?递归与深度优先搜索的关系:递归与系统栈递归调用的过程:栈的作用: 递归与系统栈的简单示例递归实现 DFS 的简…...
LeetCode题练习与总结:基本计算器 Ⅱ--227
一、题目描述 给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-2^31, 2^31 - 1] 的范围内。 注意:不允许使用任何将字符串作为数学表达式计算…...

Elasticsearch基础(七):Logstash如何开启死信队列
文章目录 Logstash如何开启死信队列 一、确保 Elasticsearch 输出插件启用 DLQ 支持 二、配置 Logstash DLQ 设置 三、查看死信队列 四、排查 CSV 到 Elasticsearch 数据量不一致的问题 Logstash如何开启死信队列 在 Logstash 中,死信队列(Dead Le…...
c语言--力扣简单题目(链表的中间节点)讲解
题目如下: 给你单链表的头结点 head ,请你找出并返回链表的中间结点。 如果有两个中间结点,则返回第二个中间结点。 示例 1: 输入:head [1,2,3,4,5] 输出:[3,4,5] 解释:链表只有一个中间结点…...
【STM32 Blue Pill编程】-定时器计数模式
定时器计数模式 文章目录 定时器计数模式1、定时器计数模式介绍2、硬件准备及接线3、模块配置3.1 定时器计数模式配置3.2 定时器中断配置3.3 串口配置4、代码实现在本文中,我们将讨论如何在计数器模式下配置 STM32 Blue Pill 定时器模块。 要将定时器用作计数器,我们将其配置…...
【例题】lanqiao1331 二进制中 1 的个数
二进制中 1 的个数 题目描述 给定一个整数 x,输出该数二进制表示中 1 的个数。 例:9 的二进制表示为 1001,有 2 位是 1 ,所以函数返回 2。 输入描述 输入 x (内存空间为 32 位的整数)。 输出描述 第一…...

【论文解读】图像序列识别:CRNN技术在场景文本识别中的应用与突破(附论文地址)
论文地址:https://arxiv.org/pdf/1507.05717 这篇文章的标题是《An End-to-End Trainable Neural Network for Image-based Sequence Recognition and Its Application to Scene Text Recognition》,作者是Baoguang Shi, Xiang Bai和Cong Yao,…...

Vue3+CesiumJS相机定位camera
new Cesium.Camera (scene) 摄像机由位置,方向和视锥台定义。 方向与视图形成正交基准,上和右视图x上单位矢量。 视锥由6个平面定义。每个平面都由 Cartesian4 对象表示,其中x,y和z分量定义垂直于平面的单位矢量,w分量…...
turbo译码算法MAX, MAX_SCALE and MAX_STAR的比较
在Turbo码的译码算法中,MAX、MAX_SCALE和MAX_STAR是涉及对数似然比(LLR)计算时,对MAP(最大后验概率)算法或其变种Log-MAP算法中分支度量计算的几种不同处理方式。下面是对这三种方法的比较: 1.…...
关于HarmonyOS的学习
day31 购物车案例 一、加入购物车 1、点击按钮后,把当前这个列表的数据拿到,应该存储到一个数组里面 --- 数据结构,把数据存储进行数组2、假如已经把所有的数据添加数组完毕,最终应该存储进购物车里面,所谓的购物车说…...

【雅特力AT32】搭建模板工程及GPIO点灯操作
目录 AT32模板工程建立及点灯操作 建立AT32模板工程 AT32点灯操作 LED原理图GPIO寄存器LED源码分析 建立AT32模板工程 从0到编译运行详细搭建保姆教程: 【雅特力AT32】Keil 环境:搭建标准库模板工程、使用 AT-Link、Debug 里选择 CMSIS-DAP调试器 下面做…...

实战千问2大模型第三天——Qwen2-VL-7B(多模态)视频检测和批处理代码测试
画面描述:这个视频中,一位穿着蓝色西装的女性站在室内,背景中可以看到一些装饰品和植物。她双手交叉放在身前,面带微笑,似乎在进行一场演讲或主持活动。她的服装整洁,显得非常专业和自信。 一、简介 阿里通义千问开源新一代视觉语言模型Qwen2-VL。其中,Qwen2-VL-72B在大…...

376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...

企业大模型服务合规指南:深度解析备案与登记制度
伴随AI技术的爆炸式发展,尤其是大模型(LLM)在各行各业的深度应用和整合,企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者,还是积极拥抱AI转型的传统企业,在面向公众…...

Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...