【C#】并行编程实战:同步原语(2)

         在第4章中讨论了并行编程的潜在问题,其中之一就是同步开销。当将工作分解为多个工作项并由任务处理时,就需要同步每个线程的结果。线程局部存储和分区局部存储,某种程度上可以解决同步问题。但是,当数据共享时,就需要用到同步原语。

        因篇幅所限,本章为第2篇。主要介绍锁、互斥锁和信号灯。

        本教程对应学习工程:魔术师Dix / HandsOnParallelProgramming · GitCode


5、锁、互斥锁和信号灯

        锁(Lock)和互斥锁(Mutex)是仅允许一个线程访问受保护资源的锁结构。信号灯(Semaphore)也是一种锁结构,它允许指定数量的线程访问受保护的资源。

同步原语

分配的线程数

跨进程

锁(Lock)

1

×

互斥锁(Mutex)

1

信号灯(Semaphore)

轻量信号灯(SemaphoreSlim)

×

        由于锁会限制其他线程访问共享资源,所以一定不要锁住会造成阻塞的代码,否则性能会大幅下降。一般来讲,锁只用于关键节

关键节(Critical Section):

        线程执行路径的一部分,必须对其进行保护以防止并发访问,进而维护某些不变性(Invariant)。关键节本身不是同步原语,但是它依赖于同步原语。

5.1、锁

        在之前的代码,我们做一下改造:

        private static object lockObj = new object();public static void RunTestAddFunctionWithLock(){TestValueA = 0;TestValueB = 0;m_IsFinishOnce = false;Task.Run(() =>{Parallel.For(0, 10000, x =>{lock (lockObj){TestValueA = x;TestValueB = x;m_IsFinishOnce = TestValueA >= TestValueB;}});Debug.Log("运行完成");});

        这个其实是非常常见的锁用法了,如上示例代码,就永远不会出现异常值的情况 m_IsFinishOnce 的值永远为 true。与 Lock 语句类似,还有一种类似写法:

Monitor.Enter(lockObj);
TestValueA = x;
TestValueB = x;
m_IsFinishOnce = TestValueA >= TestValueB;
Monitor.Exit(lockObj);

Monitor 类 (System.Threading) | Microsoft Learn提供同步访问对象的机制。 icon-default.png?t=N658https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.monitor?view=netstandard-2.1        简单来说,Lock 是 Monitor 的快捷方式,Lock 能实现的 Monitor 也都能实现。想要详细了解这两者可以扩展阅读以下连接:

Lock VS Monitor - 知乎介绍 介绍 对开发人员来说,处理关键代码部分的多线程应用程序是非常重要的。 Monitor和lock是c#语言中多线程应用程序中提供线程安全的方法(lock关键字的本质就是对Monitor的封装)。两者都提供了一种机制来确保只…https://zhuanlan.zhihu.com/p/553789674

5.2、互斥锁

        Lock 或 Mutex 只能锁定单进程,毕竟我们锁定的类只是在单进程创建的。如果出现多个进程竞争同一个资源(例如都在对同一个文档进行写入),就会报错。此时我们需要 Mutex ,创建内核级别的应用锁。

Mutex 类 (System.Threading) | Microsoft Learn还可用于进程间同步的同步基元。 icon-default.png?t=N658https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.mutex?view=netstandard-2.1        用法也很简单;

private static Mutex mutex = new Mutex(false, "TestMutex");
……
mutex.WaitOne();
TestValueA = x;
TestValueB = x;
m_IsFinishOnce = TestValueA >= TestValueB;
mutex.ReleaseMutex();

        不过我感觉,在一般的游戏开发都是单进程的逻辑,很少会需要用到互斥锁。可能在工程流水线开发的过程中,用到这个的情况多一些。

        Lock 和 Mutex 只能从获得它们的线程释放。

5.3、信号灯

        (书上称之为信号量,但微软文档上称之为信号灯,这里采用微软的说法,以下通称为信号灯)

        Lock、Monitor、Mutex 仅允许一个线程访问受保护的资源。但是有时我们也需要多个进行能够访问共享资源。例如资源池(Resource Pooling)和节流(Throttling)的应用场景都需要让多个线程能同时访问到共享资源。

        与 Lock 或 Mutex 不同,信号灯(Semaphore)是线程不可知的,这意味着任何线程都可以调用 Semaphore 的释放。信号灯也可以跨进程工作。

Semaphore 类 (System.Threading) | Microsoft Learn限制可同时访问某一资源或资源池的线程数。 icon-default.png?t=N658https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.semaphore?view=netstandard-2.1        示例代码如下:

        private static Semaphore semaphore = new Semaphore(1, 3);//初始资源量,资源总量public static void RunWithSemaphore(){Task.Run(() =>{Parallel.For(0, 10, async x =>{Debug.Log($"【{x} 】进入运行!");semaphore.WaitOne();await Task.Delay(1000);semaphore.Release();Debug.LogError($"【{x}】 完成运行!");});Debug.Log("全部运行完成");});}

        Semaphore 传入的2个参数,一个是初始资源量,一个是资源总量。像如上的代码,其实就是一个等一个,每隔 1s 执行完成一个任务:

         如果将 initialCount 设置为 2 ,这就是一次执行两个任务。这个原理其实就是 PV操作 的意思,通过信号灯来动态控制线程的阻塞与执行。

全局信号灯(Global Semaphore):

        对于操作系统是全局的,应用了内核级别的锁原语。使用名称创建(创建时进行了命名)的任何信号灯都将创建为全局信号灯,否则则为局部信号灯。


(未完待续)

 本教程对应学习工程:魔术师Dix / HandsOnParallelProgramming · GitCode

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

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

相关文章

elasticsearch插件ik分词器,无法启动解决方案

首先7以后的版本一定要与es的版本保持一致下载包只能下载这个路径的文件,版本号与自己的es版本保持一致 https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v8.6.0/elasticsearch-analysis-ik-8.6.0.zip这里可以直接替换 docker容器无法启动&…

Spark-用IDEA编写wordcount demo

配置 Spark版本&#xff1a;3.2.0 Scala版本&#xff1a;2.12.12 JDK&#xff1a;1.8 Maven&#xff1a;3.6.3 pom文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi&quo…

Coggle 30 Days of ML (23年7月)任务二:数据可视化

Coggle 30 Days of ML (23年7月&#xff09;任务二&#xff1a;数据可视化 任务二&#xff1a;对数据集字符进行可视化&#xff0c;统计标签和字符分布 说明&#xff1a;在这个任务中&#xff0c;需要使用Pandas库对数据集的字符进行可视化&#xff0c;并统计数据集中的标签和…

[PyTorch][chapter 44][时间序列表示方法4]

前言&#xff1a; 训练复杂度 OE*T*Q 参数 全称 E 迭代次数 Number of the training epochs T数据集大小 Number of the words in the training set Q 模型计算复杂度 Model computational complexity E,T 一般都认为相同&#xff0c;所以这里面主要讨论Q&#xff0c;模…

驱动day6

驱动程序 #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/mod_devicetable.h> #include …

LayUI 实现二级导航栏

目录 实现步骤&#xff1a; 1. 分析数据库 2. 构建数据源 2.1 编写实体类 2.2 编写节点实体类 2.3 构建BuildTree节点结构方法类 2.4 编写dao类 2.5 编写数据Acntion控制类 3. 前台准备 3.1 配置mvc.xml文件 3.2 页面编写 3.3 运行效果 实现步骤&#xff1a; 1. 分…

Linux系统使用(超详细)

目录 Linux操作系统简介 Linux和windows区别 Linux常见命令 Linux目录结构 Linux命令提示符 常用命令 ls cd pwd touch cat echo mkdir rm cp mv vim vim的基本使用 grep netstat Linux面试题 Linux操作系统简介 Linux操作系统是和windows操作系统是并列…

在线试用Stable Diffusion生成可爱的图片

文章目录 一、 Stable Diffusion 模型在线使用地址&#xff1a;二、模型相关版本和参数配置&#xff1a;三、图片生成提示词与反向提示词&#xff1a;提示词1提示词2提示词3提示词4提示词5 一、 Stable Diffusion 模型在线使用地址&#xff1a; https://inscode.csdn.net/insc…

centos7 环境下部署 nacos单机模式

1、官网下载 nacos 官网地址&#xff1a;home 去github上下载nacos-server。我下载的是 nacos-server-1.4.1.tar.gz 2、安装 nacos 下载完成后&#xff0c;将安装包上传到 centos 创建 nacos 目录&#xff08;安装位置任意&#xff09; mkdir -p /usr/local/nacos解压 nac…

实战打靶集锦-021-glasgowsmile

提示&#xff1a;本文记录了博主的一次曲折的打靶经历。 目录 1. 主机发现2. 端口扫描3. 服务枚举4. 服务探查4.1 手工访问4.2 目录枚举4.3 手工探查4.4 搜索EXP4.5 joomlascan4.6 用户猜测与密码爆破4.7 构建反弹shell 5. 提权5.1 优化shell5.2 枚举系统信息5.3 探查/etc/pass…

第24章:事务基础知识

一、数据库事务Transactions 1.为什么要使用事务 事务可以让数据库保持一致性&#xff0c;通过事务的机制恢复到某个时间点&#xff0c;即使系统崩溃数据库修改的数据不会丢失。 2.存储引擎支持事务的情况 命令: show engines; 只有InnoDB支持事务 3.事务基本概念 事务&a…

【Distributed】分布式ELK日志文件分析系统(一)

文章目录 一、ELK 概述1. 为什么要使用 ELK2. 完整日志系统基本特征3. ELK 简介3.1 ElasticSearch&#xff08;ES&#xff09;3.2 Kiabana3.3 Logstash3.4 其它组件Filebeat缓存/消息队列Fluentd 4. ELK 的工作原理5. Linux 系统内核日志消息的优先级别 二、 部署 ELK 集群服务…