c#多线程中使用SemaphoreSlim

        SemaphoreSlim是一个用于同步和限制并发访问的类,和它类似的还有Semaphore,只是SemaphoreSlim更加的轻量、高效、好用。今天说说它,以及如何使用,在什么时候去使用,使用它将会带来什么优势。

代码的业务是:

在多线程下进行数据的统计工作,简单点的说就是累加数据。

1.首先我们建立一个程序

代码如下

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static int a = 0;
        static async Task Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Task t1 = Task.Run(() =>
            {
                A();
            });

            Task t2 = Task.Run(() =>
            {
                B();
            });
            await Task.WhenAll(t1, t2);
            stopwatch.Stop();
            Console.WriteLine("总时间:" + stopwatch.ElapsedMilliseconds);
            Console.WriteLine("总数:" + a);
            Console.ReadLine();
        }
        private static void A()
        {
            for (int i = 0; i < 100_0000; i++)
            {
                a++;
            }
        }

        private static void B()
        {
            for (int i = 0; i < 100_0000; i++)
            {
                a++;
            }
        }

    }
}

2.运行结果

此时需要多运行几次,会发现,偶尔出现运行的结果不一样,这就是今天的问题 

3.分析结果

从结果看,明显错误了,正确答案是:200 0000,但是第二次的结果是131 6465。我们的业务就是开启2个线程,一个A方法,一个B方法,分别对a的数据进行累加计算。那么为什么造成这样的结果呢?

造成这样的原因就是多线程的问题。

解决方法一:

1.使用lock

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static int a = 0;
        static object o = new object();
        static async Task Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Task t1 = Task.Run(() =>
            {
                A();
            });

            Task t2 = Task.Run(() =>
            {
                B();
            });
            await Task.WhenAll(t1, t2);
            stopwatch.Stop();
            Console.WriteLine("总时间:" + stopwatch.ElapsedMilliseconds);
            Console.WriteLine("总数:" + a);
            Console.ReadLine();
        }
        private static void A()
        {
            for (int i = 0; i < 100_0000; i++)
            {
                lock (o)
                {
                    a++;
                }
            }
        }

        private static void B()
        {
            for (int i = 0; i < 100_0000; i++)
            {
                lock (o)
                {
                    a++;
                }
            }
        }

    }
}

2. lock的结果

当我们增加lock后,不管运行几次,结果都是正确的。 

解决方法二:

1.使用SemaphoreSlim

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static int a = 0;
        static object o = new object();
        static SemaphoreSlim semaphore = new SemaphoreSlim(1);  //控制访问线程的数量
        static async Task Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Task t1 = Task.Run(() =>
            {
                A();
            });

            Task t2 = Task.Run(() =>
            {
                B();
            });
            await Task.WhenAll(t1, t2);
            stopwatch.Stop();
            Console.WriteLine("总时间:" + stopwatch.ElapsedMilliseconds);
            Console.WriteLine("总数:" + a);
            Console.ReadLine();
        }
        private static void A()
        {
            for (int i = 0; i < 100_0000; i++)
            {
                semaphore.Wait();
                //lock (o)
                //{
                a++;
                //}
                semaphore.Release();
            }
        }

        private static void B()
        {
            for (int i = 0; i < 100_0000; i++)
            {
                semaphore.Wait();
                //lock (o)
                //{
                a++;
                //}
                semaphore.Release();
            }
        }

    }
}

2.SemaphoreSlim的效果

当我们增加SemaphoreSlim后,不管运行几次,结果都是正确的。  

4.我们对比方法一和方法二发现,他们的结果都是一样的,但是lock似乎比SemaphoreSlim更加的高效,是的,lock解决此业务的确比SemaphoreSlim高效。但是lock能干的事,SemaphoreSlim肯定能干,SemaphoreSlim能干的事,lock不一定能干。

5.SemaphoreSlim的使用

SemaphoreSlim使用的范围非常的广,可以限制访问资源的线程数,例如限制一个资源最多5个线程可以同时访问

using System.Threading;

class Program
{
    static SemaphoreSlim semaphore = new SemaphoreSlim(5); // 允许最多5个线程同时访问资源

    static void Main()
    {
        for (int i = 0; i < 10; i++)
        {
            Task.Run(DoWork);
        }
        Console.ReadLine();
    }

    static async Task DoWork()
    {
        await semaphore.WaitAsync(); // 等待许可
        try
        {
            Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 开始工作");
            await Task.Delay(1000); // 模拟耗时操作
            Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 结束工作");
        }
        finally
        {
            semaphore.Release(); // 释放许可
        }
    }
}

此时DoWork()这个方法,最多同时只有5个线程访问,当改成1个,就是按照顺序进行了,和lock的使用是一样的 

6.总结

        如果需要确保同一时间只有一个线程访问某资源(此案例指的就是变量a),那么可以使用Lock,也可以使用SemaphoreSlim;如果需要控制同时访问资源的线程数量,并且需要更复杂的信号量操作,那么可以使用SemaphoreSlim。总之,使用Lock还是SemaphoreSlim,都是根据具体业务而定。

拓展:

当我们在第1步,只需要增加一句话,不增加lock和SemaphoreSlim,依然可以使得计算的结果准确,那就是增加

Console.WriteLine(a);

充当了线程同步的作用 

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static int a = 0;
        static async Task Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            Task t1 = Task.Run(() =>
            {
                A();
            });

            Task t2 = Task.Run(() =>
            {
                B();
            });
            await Task.WhenAll(t1, t2);
            stopwatch.Stop();
            Console.WriteLine("总时间:" + stopwatch.ElapsedMilliseconds);
            Console.WriteLine("总数:" + a);
            Console.ReadLine();
        }
        private static void A()
        {
            for (int i = 0; i < 10_0000; i++)
            {
                a++;
                Console.WriteLine(a);
            }
        }

        private static void B()
        {
            for (int i = 0; i < 10_0000; i++)
            {
                a++;
                Console.WriteLine(a);
            }
        }

    }
}

效果

本文源码:

https://download.csdn.net/download/u012563853/88714363

本文来源:

c#多线程中使用SemaphoreSlim-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用统计鸟网站统计分析网站流量来源?

统计鸟官网地址&#xff1a;https://www.tongjiniao.com/ 站长必备&#xff01;网站数据统计&#xff0c;流量监测平台 提供网站数据统计分析、搜索关键词、流量访问来源等服务 深入分析用户点击习惯&#xff0c;为智能化运营网站提供更好的用户体验 目录 一、注册账号信息 二…

Ubuntu开机自动挂载硬盘

前言&#xff1a; 因为我的电脑是WIN10 Ubuntu18.04双系统&#xff0c;且两个系统都装在C盘上&#xff0c;而D盘作为数据和代码存储盘&#xff0c;经常会开机就被访问&#xff0c;例如上一次关机前用VS Code访问D盘代码&#xff0c;然后下一次开机的时候打开VSCode发现打不开…

Spark SQL基础

SparkSQL基本介绍 什么是Spark SQL Spark SQL是Spark多种组件中其中一个,主要是用于处理大规模的结构化数据 什么是结构化数据: 一份数据, 每一行都有固定的列, 每一列的类型都是一致的 我们将这样的数据称为结构化的数据 例如: mysql的表数据 1 张三 20 2 李四 15 3 王五 1…

用通俗易懂的方式讲解:一文讲透主流大语言模型的技术原理细节

大家好&#xff0c;今天的文章分享三个方面的内容&#xff1a; 1、比较 LLaMA、ChatGLM、Falcon 等大语言模型的细节&#xff1a;tokenizer、位置编码、Layer Normalization、激活函数等。 2、大语言模型的分布式训练技术&#xff1a;数据并行、张量模型并行、流水线并行、3D …

基于JAVA的数据可视化的智慧河南大屏 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏&#xff0c;包含了GDP、…

Mysql如何优化慢查询

如何优化慢查询 慢 SQL 的优化&#xff0c;主要从两个方面考虑&#xff0c;SQL 语句本身的优化&#xff0c;以及数据库设计的优化。 1、避免不必要的列 覆盖索引会导致回表&#xff0c;且增大了IO 2、分页优化 深分页解决方案 使用子查询in 使用连接表 left join 使用游标&a…

uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -投票帖子管理实现

锋哥原创的uniapp微信小程序投票系统实战&#xff1a; uniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )_哔哩哔哩_bilibiliuniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )共计21条视频…

131基于matlab的差分进化算法优化K均值聚类问题

基于matlab的差分进化算法优化K均值聚类问题&#xff0c;可调整K参数得到最佳聚类结果。输出聚类可视化图和优化迭代曲线。可替换自己的数据&#xff0c;程序已调通&#xff0c;可直接运行。 131matlab差分进化算法K均值聚类 (xiaohongshu.com)

解决虚拟机的网络图标不见之问题

在WIN11中&#xff0c;启动虚拟机后&#xff0c;发现网络图标不见了&#xff0c;见下图&#xff1a; 1、打开虚拟机终端 输入“sudo server network-manager stop”&#xff0c;停止网络管理器 输入“cd /回车” &#xff0c; 切换到根目录 输入“cd var回车” &#xff0c;…

一款快速稳定的漏洞扫描工具【afrog】零基础入门到精通

工具介绍 afrog 是一款快速、稳定的高性能漏洞扫描器。支持用户自定义PoC&#xff0c;内置CVE、CNVD、默认密码、信息泄露、指纹识别、越权访问、任意文件读取、命令执行等多种类型。通过afrog&#xff0c;网络安全专业人员可以快速验证和修复漏洞&#xff0c;这有助于增强他们…

chromedriver 114以后版本下载地址

谷歌浏览器版本经常会升级&#xff0c;chromedriver 也得下载匹配的版本 chromedriver 114以前版本下载地址https://registry.npmmirror.com/binary.html?pathchromedriver/ 找到匹配浏览器版本 查看自己浏览器版本号v120.0 v120.0版本chromedriver下载地址https://google…

impala元数据自动刷新

一.操作步骤 进入CM界面 > Hive > 配置 > 搜索 启用数据库中的存储通知(英文界面搜索&#xff1a;Enable Stored Notifications in Database)&#xff0c;并且勾选&#xff0c;注意一定要勾选&#xff0c;配置后面的配置不生效。数据库通知的保留时间默认为2天&#…

二阶贝塞尔曲线生成弧线

概述 本文分享一个二阶贝塞尔曲线曲线生成弧线的算法。 效果 实现 1. 封装方法 class ArcLine {constructor(from, to, num 100) {this.from from;this.to to;this.num num;return this.getPointList();}getPointList() {const { from, to } thisconst ctrlPoint thi…

idea使用docker-compose发布应用程序

非常重要的话说在前头 idea要想使用docker-compose&#xff0c;不能使用ssh创建idea Docker&#xff0c;而需要使用socket创建idea Docker。 socket docker是不安全的&#xff0c;任何人都可以访问你的docker&#xff0c;所以只能测试环境使用&#xff0c;请勿在正式环境使用s…

RT-Thread SMP介绍与移植

SMP&#xff1a;&#xff08;Symmetrical Multi-Processing&#xff09;对称多处理&#xff0c;简称SMP&#xff0c;是指在一个计算机上汇集了一组处理器&#xff08;多CPU&#xff09;&#xff0c;各CPU之间共享内存子系统以及总线结构。 RT-Thread自v4.0.0版本开始支持SMP&a…

超级好看的个人主页源码

源码介绍 超级好看的个人主页源码HTML,使用了 HTML、CSS 和 JavaScript 技术&#xff0c;带音乐播放器 需要修改什么到代码里面自行修改,记事本就可以打开&#xff0c;总之&#xff0c;这个个人主页源码非常漂亮和实用&#xff0c;使用了许多现代的 Web 技术来创建一个响应式、…

个人网站制作 Part 3 用JS添加高级交互(表单验证、动态内容更新) | Web开发项目

文章目录 &#x1f469;‍&#x1f4bb; 基础Web开发练手项目系列&#xff1a;个人网站制作&#x1f680; 使用JavaScript进行交互&#x1f528;表单验证&#x1f527;步骤 1: 添加JavaScript文件&#x1f527;步骤 2: 更新表单HTML &#x1f528;动态内容更新&#x1f527;步骤…

API设计:从基础到最佳实践

1*vWvkkgG6uvgmJT8GkId98A.png 在这次深入探讨中&#xff0c;我们将深入了解API设计&#xff0c;从基础知识开始&#xff0c;逐步进阶到定义出色API的最佳实践。 作为开发者&#xff0c;你可能对许多这些概念很熟悉&#xff0c;但我将提供详细的解释&#xff0c;以加深你的理解…

冷风机系统的拟定和制冷循环的热力计算

一&#xff0c;冷风机系统的概述 单元式冷风机是一种不带加湿器的非热泵型单元式空气调节机&#xff0c;只能用于夏季降温&#xff0c;按标准 GB/T 17758-1999《单元式空气调节机组》的规定1&#xff0c;单元式冷风机应能使室内温度保持在(18℃-25℃)1℃&#xff0c;相对温度 (…

数据洞察力,驱动企业财务变革

我们不得不面对一个现实&#xff0c;就是数据量的剧增。加上大部分企业并不愿意删除历史数据&#xff0c;以防未来预测分析时需要&#xff0c;这造成数据就像一个雪球&#xff0c;越滚越大。然而&#xff0c;过多的数据和数据不足一样会成为企业发展和理解分析的障碍。从海量数…