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

news2024/12/28 5:11:07

文章目录

  • 前言
  • 一、异步互斥锁的作用是什么?
    • 示例一、创建和销毁
  • 二、如何实现?
    • 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()
{
    //超时等待300ms
    bool 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.coloradmin.cn/o/1394808.html

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

相关文章

SpringBoot基础:一步步创建SpringBoot工程

摘要 本文介绍了&#xff0c;从零开始创建SpringBoot工程&#xff0c;且在每一步给出分析和原因。创建maven – 转Springboot – 引入jdbc – 引入数据库操作框架&#xff0c;最后给出了不同场景指定不同配置文件的方案。 背景 为什么要使用SpringBoot工程&#xff1f; 使用Sp…

YOLOv8全网首发:DCNv4更快收敛、更高速度、更高性能,效果秒杀DCNv3、DCNv2等 ,助力检测

💡💡💡本文独家改进:DCNv4更快收敛、更高速度、更高性能,完美和YOLOv8结合,助力涨点 DCNv4优势:(1) 去除空间聚合中的softmax归一化,以增强其动态性和表达能力;(2) 优化存储器访问以最小化冗余操作以加速。这些改进显著加快了收敛速度,并大幅提高了处理速度,DCN…

X Winner受邀出席泰国政府加密峰会,上演未来独角兽的独角戏

​在近日&#xff0c;游戏化流动性&#xff08;Gamified Liquidity&#xff09;的基础设施 X WINNER &#xff0c;受邀出席了由泰国政府举办的“Blockchain to Government Conference ”大会。据了解&#xff0c;该会议是泰国政府布局 Web3 产业的一个重要标志&#xff0c;同时…

《WebKit 技术内幕》之四(2): 资源加载和网络栈

2.Chromium 多进程资源加载 2,1 多进程 资源的实际加载在各个WebKit移植中有不同的实现。Chromium采用的多进程的资源加载机制。 ResourceHandle 类之下的部分是不同移植对获取资源的不同实现&#xff0c;Chromium 中是 多进程资源加载 。主要是多个Renderer进程和Browser进程…

【汽车销售数据】2015~2023年各厂商各车型的探索 数据分析可视化

数据处理的思路&#xff1a; 1 各表使用情况&#xff1a; 汽车分厂商每月销售表&#xff0c;该表主要分析展示top10销量的厂商销量、占比变化情况&#xff08;柱形图、饼图&#xff09;&#xff1b;中国汽车分车型每月销售量表&#xff0c;该表主要分析展示top20销量的车型销…

五、基础篇 vue列表渲染

在v-for里使用对象用 v-for 把一个数组对应为一组元素 我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in list形式的特殊语法&#xff0c;其中 list是源数据数组&#xff0c;而 item 则是被迭代的数组元素的别名。 <template><div clas…

Cuda与Torch配置(For 集群服务器)超详细步骤

每次配置模型环境&#xff0c;无论是在windows&#xff0c;linux&#xff0c;集群服务器上都会在这里卡一段&#xff0c;为了未来配置方便&#xff0c;记录下配置注意事项 配置cuda和torch主要有几个要点&#xff0c;分别是&#xff1a; 显卡与驱动&#xff08;NIVIADA drive…

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

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

Arthas项目实战用法

一、简介 1、项目所在位置 2、安装Arthas 1. 下载arthas-boot.jar 2. 运行arthas-boot.jar 3. 选择进程 3、卸载Arthas 二、核心监视功能 1、monitor&#xff1a;监控方法的执行情况 2、watch&#xff1a;检测函数返回值 3、trace&#xff1a;根据路径追踪&#xff0c;…

java转义字符

//转义字符的使用 public class ChangeChar{//编写一个main方法public static void main(String[] args){// \t :一个制表位&#xff0c;实现对齐的功能System.out.println("北京\t天津\t上海");// \n :换行符&#xff0c;实现换行System.out.println("jack\nsm…

【OJ】牛客链表刷题

题目 1. 链表分割1.1 题目分析1.2 代码 2. 链表的回文结构2.1 题目分析2.2 代码 这里两道与链表有关的题目均来自牛客。 1. 链表分割 1.1 题目分析 因为这里代码不能选择用c语言写&#xff0c;所以选择用c,因为c兼容c。 题目要求分割链表&#xff0c;我们可以直接弄成两个带哨…

【笔记】Helm-3 主题-6 Chart仓库指南

Chart仓库指南 本节介绍如何创建和使用chart仓库。在高层级中&#xff0c;chart仓库是打包的chart存储和分享的位置。 社区的Helm chart仓位于 Artifact Hub &#xff0c;欢迎加入。不过Helm也可以创建并运行您自己的chart仓库。该指南将介绍如何操作。 Artifact Hub 先决条…

威士忌的品鉴之旅麦芽制备:从浸泡、发芽到干燥

麦芽制备是威士忌酿造过程中至关重要的一环&#xff0c;它直接影响到产品的品质和风味。麦芽的制备包括浸泡、发芽、干燥等环节&#xff0c;每个环节都需要严格控制温度、湿度和时间等细节。本文将深入探讨麦芽制备的过程&#xff0c;以雷盛537威士忌&#xff0c;分析麦芽制作过…

4人遇难,北京突发火情 富维烟火识别防止悲剧再次发生

在北京一处居民区&#xff0c;一场突如其来的火灾夺走了四条宝贵的生命。火情迅速蔓延&#xff0c;烟雾弥漫&#xff0c;居民们猝不及防。这一悲剧再次提醒我们&#xff0c;火灾预防和早期识别的重要性不容忽视。 在这样的背景下&#xff0c;北京富维图像公司开发的FIS智能图像…

肯尼斯·里科《C和指针》第7章 函数(2)递归

7.5 递归 C通过运行时堆栈支持递归函数的实现。递归函数就是直接或间接调用自身的函数。许多教科书都把计算阶乘和斐波那契数列用来说明递归&#xff0c;这是非常不幸的。在第1个例子中&#xff0c;递归并没有提供任何优越之处。在第2个例子中&#xff0c;它的效率之低是非常恐…

中国多家半导体设备厂表现出色,营收可观 | 百能云芯

多家中国半导体设备大厂近日相继发布了2023年度业绩预告&#xff0c;表现出色&#xff0c;营收和净利润均呈现较大幅度的增长&#xff0c;这一利好消息背后得益于半导体行业周期的复苏以及国产半导体需求的持续增长。 据百能云芯电子元器件商城了解&#xff0c;北方华创发布的公…

域中的主机报错1231解决办法

1、 这个一般是使用 net view /domain 的时候出现的报错&#xff0c;若是报错 6118 只需在域控制器中开启 computer browser 服务即可 2、解决了 6118 问题后&#xff0c;在域控制器上已经能使用 net view /domain 了&#xff0c;但是域中的其它主机上会出现 1231 问题&#xf…

单臂路由【新华三与华为区别】

【技术介绍】单臂路由&#xff0c;简单来讲&#xff0c;就是在一个路由器的物理端口上虚拟几个虚拟端口&#xff0c;从而达到节约资源的效果 【背景】 R1上进行单臂路由 【操作】 【华为】 [HW-SWA] vlan batch 10 20 int g0/0/1 port link-type access port default vlan 10…

揭露欧拉骗局5.构建函数1/n>ln(1+1/n)公开作弊

构建函数又名构造函数&#xff0c;是欧系数学的重要解题方法。构建函数最成功的范例是“1/n&#xff1e;ln(11/n)”&#xff0c;它来自欧拉&#xff0c;其手段极其卑劣下作&#xff01;随心所欲、目空一切是欧拉的习惯 欧拉证明1/n&#xff1e;ln(11/n)的操作有两步&#xff1…