C# 实现单线程异步互斥锁

文章目录

  • 前言
  • 一、异步互斥锁的作用是什么?
    • 示例一、创建和销毁
  • 二、如何实现?
    • 1、标识
      • (1)标识是否锁住
      • (2)加锁
      • (3)解锁
    • 2、异步通知
      • (1)创建对象
      • (2)返回Task
      • (3)通知完成
    • 3、等待队列
      • (1)创建队列
      • (2) 等待加锁
      • (3)加锁成功
  • 三、完整代码
  • 四、使用示例
    • 1、基本用法
    • 2、尝试加锁
    • 3、加锁对比
      • (1)未加锁
      • (2)加锁
  • 总结


前言

C#对异步的支持越来越成熟,async、await简化了代码也提高了可读性,但由于在一段上下文中有了异步操作,意味着这段操作可能会被同时重复调用,如果本身没有被设计可以重复调用的情况下,就很可能会出问题。


一、异步互斥锁的作用是什么?

异步互斥锁的作用是用于确保存在异步操作的上下文同步互斥。可以参考flutter的插件mutex功能与本文基本一样。

示例一、创建和销毁

有创建和销毁两个方法,两个方法中都有异步操作,两个方法可以单独调用,但不可以同时调用。
单线程中连续调用创建和销毁(不在同一个上下文无法用await),如果没有互斥限制有可能出现如下的操作:

创建开始->创建异步操作->消息队列->销毁开始->销毁异步操作->消息队列->销毁完成->消息队列->创建完成

加入异步互斥锁之后

加锁->创建开始->创建完成->解锁
加锁等待->销毁开始->销毁完成->解锁

二、如何实现?

由于操作都是在单线程我们直接用标识+队列就可以实现一个互斥锁。

1、标识

(1)标识是否锁住

bool _lock = false;

(2)加锁

_lock=true;

(3)解锁

_lock=false;

2、异步通知

通过TaskCompletionSource可以实现异步通知

(1)创建对象

var tcs = new TaskCompletionSource();

(2)返回Task

return tcs.Task;

(3)通知完成

tcs.SetResult();

3、等待队列

用一个队列来记录等待加锁的请求。

(1)创建队列

Queue<TaskCompletionSource> _queue = new Queue<TaskCompletionSource>();

(2) 等待加锁

_queue.Enqueue(tcs);

(3)加锁成功

_queue.Dequeue().SetResult();

三、完整代码

/// <summary>
/// 异步锁,非线程锁,只能用于单线程异步环境中。
/// </summary>
class AsyncMutex
{Queue<TaskCompletionSource> _queue = new Queue<TaskCompletionSource>();bool _lock = false;/// <summary>/// 获取锁/// </summary>/// <returns>返回Task,await后即进入了锁</returns>public Task Acquire(){if (_lock){var tcs = new TaskCompletionSource();_queue.Enqueue(tcs);return tcs.Task;}_lock = true;return Task.CompletedTask;}/// <summary>/// 尝试获取锁/// 因为是单线程环境,重复调用需要切换上下文,否则是无法成功的。/// 比如可以await Task.Delay(30);/// </summary>/// <returns>是否成功</returns>public bool TryAcquire(){if (_lock) return false;return _lock = true;}/// <summary>/// 释放锁/// </summary>public void Release(){if (_queue.Count > 0){_queue.Dequeue().SetResult();}else{_lock = false;}}
}

四、使用示例

1、基本用法

直接加锁

AsyncMutex _mtx = new AsyncMutex();
async void test()
{await _mtx.Acquire();//custom code_mtx.Release();
}

2、尝试加锁

加锁成功才执行操作

AsyncMutex _mtx = new AsyncMutex();
void test()
{if (_mtx.TryAcquire()){//custom code_mtx.Release();}
}

超时等待

AsyncMutex _mtx = new AsyncMutex();
async void test()
{//超时等待300msbool isLock = false;for (int i = 0; i < 10; i++){if (isLock = _mtx.TryAcquire()) break;await Task.Delay(30);}if (isLock){//custom code_mtx.Release();}
}

3、加锁对比

(1)未加锁

async void test(int num)
{Console.WriteLine("enter " + num);//模拟异步操作await Task.Delay(10);Console.WriteLine("exit " + num);
}
//.net 6.0
test(1);
test(2);
test(3);

可能出现的组合,效果预览
在这里插入图片描述

(2)加锁

AsyncMutex _mtx = new AsyncMutex();
async void test(int num)
{await _mtx.Acquire();Console.WriteLine("enter " + num);//模拟异步操作await Task.Delay(10);Console.WriteLine("exit " + num);_mtx.Release();
}
//.net 6.0
test(1);
test(2);
test(3);

效果预览
在这里插入图片描述


总结

以上就是今天要讲的内容,本文简单的实现了单线程的异步互斥锁,实现起来相对简单,但作用还是比较大的。虽然说有些情况的异步是可以在前期设计上避免同时调用,比如登录按钮点击后出现蒙板不允许再次点击,但是对于已存在的代码出现了同时调用问题,此时有互斥锁则可以避免大范围改动代码,有效解决问题。

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

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

相关文章

Python单元测试之pytest的使用

一、前提准备 1、前提&#xff1a;需要安装pytest和pytest-html(生成html测试报告&#xff09; pip install pytest 和 pip install pytest-html 安装插件&#xff1a;pip install 插件名 2、命名规范 Pytest单元测试中的类名和方法名必须是以test开头,执行中只能找到test开…

Spring(19) ThreadPoolTaskExecutor 线程池的使用

目录 一、线程池简介1.1 为什么使用线程池1.2 线程池为什么需要使用队列1.3 线程池为什么要使用阻塞队列而不是用非阻塞队列1.4 如何配置线程池1.5 execute() 和 submit() 方法 二、ThreadPoolTaskExecutor 线程池简介2.1 简介2.2 核心参数配置2.3 ThreadPoolTaskExecutor 内部…

数据库作业三

1.创建student和score表 2.为student表和score表增加记录 3.查询student表的所有记录 4.查询student表的第2条到4条记录 5.从student表查询所有学生的学号&#xff08;id&#xff09;、姓名&#xff08;name&#xff09;和院系&#xff08;department&#xff09;的信息 6.从st…

flutter3使用dio库发送FormData数据格式时候的坑,和get库冲突解决办法

问题描述 问题1&#xff1a;当你使用FormData.from(Flutter3直接不能用)的时候&#xff0c;可能会提示没有这个方法&#xff0c;或者使用FormData.fromMap(flutter3的dio支持)的时候也提示没有&#xff0c;这时候可能就是和get库里面的Formdata冲突了 问题1&#xff1a;The me…

vue基于spring boot框架的发艺美发店理发店管理系统的设计q9xpe

店铺信息、美发信息是发艺美发店管理系统的重要组成部分&#xff0c;信息清晰、详细、准确&#xff0c;能够有效地促进发艺美发店管理系统的运行[5]。基础设定函数是对整个系统的总体布局进行合理安排&#xff0c;包括&#xff1a;店铺活动、物品信息、领用信息等。通过对各类资…

【C++】vector模拟实现过程中值得注意的点

Hello大家好&#xff01;我是咕噜的铁蛋&#xff01;C中的vector是一种动态数组&#xff0c;它能够根据需要自动增长和缩小。虽然C标准库已经为我们提供了vector的实现&#xff0c;但在某些情况下&#xff0c;我们可能需要自己模拟实现一个类似于vector的数据结构。今天铁蛋将给…

CPU密集型计算、IO密集型计算、多进程、多线程

参考链接&#xff1a; 使用多进程multiprocessing模块加速程序的运行_哔哩哔哩_bilibili 什么是CPU密集型计算、IO密集型计算&#xff1a; CPU密集型&#xff1a; CPU密集型也叫计算密集型&#xff0c;是指I/O在很短的时间就可以完成&#xff0c;CPU需要大量的计算和处理&a…

javaweb学习day01(HTML)

一、B/S 软件开发架构简述 1 Java Web 技术体系图 2 B/S 软件开发架构简述 B/S架构 前端 后端 数据库 二、HTML 1 官方文档 地址: https://www.w3school.com.cn/html/index.asp 离线文档: W3School 离线手册(2017.03.11 版).chm 2 网页 3 HTML 介绍 3.1 HTML 是什么…

oop面向对象编程python

1.定义一个圆类&#xff08;Circle&#xff09;,求圆的面积和周长 import mathclass Circle():def __init__(self, R ,name):self.radius Rself.name namedef girth(self):return 2 * self.radius * math.pidef area(self):return self.radius ** 2 * math.pic1 Circle(10,&q…

Docker之nacos集群部署

前言 Nacos 是一个开源的注册中心和配置中心&#xff0c;用于实现微服务架构中的服务发现、服务治理和动态配置管理。在 Docker 中使用 Nacos&#xff0c;你可以通过拉取官方提供的 Docker 镜像并运行容器的方式来快速部署 一.Nacos镜像拉取及独立模式 镜像拉取命令详解&…

【自动化测试】看完这篇文章,让你了解到你和大厂的差距到底在哪儿

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

sc.pl.umap 画feature plot

今天有时间尝试测试了这个scanpy的feature plot,其实很简单&#xff0c;就是使用 sc.pl.umap(adata,color"gene name"), 但是这个地方就有一个问题&#xff0c;这个画出来的值是原始的基因值还是scale之后的&#xff0c;这个我得搞清楚 首先看使用例子&#xff0c;参…

企业为什么需要选择软件测试外包公司?有哪些查找途径?

软件测试外包公司是专门为软件企业提供软件测试服务的机构。这些公司拥有一支经验丰富、专业化的软件测试团队&#xff0c;能够帮助企业进行软件产品的测试、评估和质量保证。 一、企业为什么需要选择软件测试外包公司?   1.专业的测试团队&#xff1a;外包公司拥有经验丰富…

赤藓糖醇行业研究:预计2029年将达到3.5亿美元

赤藓糖醇是一种四碳糖醇&#xff0c;存在于多种食物中&#xff0c;如葡萄、梨、西瓜等&#xff0c;可由微生物发酵法和化学合成法两种方法制备&#xff0c;目前商业化生产中均采用微生物发酵法。赤藓糖醇由葡萄糖发酵制作而成&#xff0c;上游原料主要包括葡萄糖、玉米淀粉糖和…

C++(13)——string

上篇文章中介绍了中部分函数的用法&#xff0c;本篇文章将继续对其他的函数进行介绍&#xff1a; 1. substr: string substr (size_t pos 0, size_t len npos) const; 函数的两个参数如上述代码所示&#xff0c;此函数的主要作用是根据一个已有的的对象的起始坐标开始&a…

STM32---基本定时器(含源码)小白可入

写在前面&#xff1a;定时器是STM32中一个十分重要的外设&#xff0c;并且在STM32中具有多个定时器。定时器的包括基本定时器、通用定时器以及高级控制定时器&#xff0c;这些定时器相关独立&#xff0c;不共享任何资源。当然&#xff0c;其难易程度也是逐渐增加的&#xff0c;…

如何实现固定公网地址远程访问本地部署的Termux MySQL数据库

文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 前言 Android作为移动设备&#xff0c;尽管最初并非设计为服务器&#xff0c;但是随着技术的进步我们可以将Android配置为生产力工具&#xff0c;变成一个随身…

postman测试文件上传接口设置说明

Postman介绍及下载链接地址 Download Postman | Get Started for Free 打开postman 选择POST方法&#xff0c;然后设置goform 设置Header参数 设置Body参数&#xff0c;选择数据form-data 添加文件&#xff0c; 选择为文件属性 添加需要上传的文件

MySql三方工具

Yearning 1.1.1 Yearning简介 Yearning 开源的MySQL SQL语句审核平台&#xff0c;提供数据库字典查询&#xff0c;查询审计&#xff0c;SQL审核等多种功能。 Yearning 1.x 版本需Inception提供SQL审核及回滚功能。 Inception是集审核&#xff0c;执行&#xff0c;回滚于一体的…

封装日期时间组件

概述 该组件包含日期选择&#xff0c;任意时间选择、固定时间点选择。 子组件代码(date-picker.vue) <template><div id"date_picker"><el-popover placement"top" width"322" trigger"click" ref"popover&quo…