当前位置: 首页 > news >正文

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);

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


总结

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

相关文章:

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

文章目录 前言一、异步互斥锁的作用是什么&#xff1f;示例一、创建和销毁 二、如何实现&#xff1f;1、标识&#xff08;1&#xff09;标识是否锁住&#xff08;2&#xff09;加锁&#xff08;3&#xff09;解锁 2、异步通知&#xff08;1&#xff09;创建对象&#xff08;2&a…...

Java设计模式中策略模式可以解决许多if-else的代码结构吗? 是否能满足开闭原则?

Java设计模式中策略模式可以解决许多if-else的代码结构吗&#xff1f; 是否能满足开闭原则&#xff1f; 是的&#xff0c;策略模式可以帮助解决许多if-else的代码结构。通过将不同的算法封装成不同的策略类&#xff0c;然后在需要的时候动态地切换策略&#xff0c;可以避免使…...

[C#]C# winform部署yolov8目标检测的openvino模型

【官方框架地址】 https://github.com/ultralytics/ultralytics 【openvino介绍】 OpenVINO&#xff08;Open Visual Inference & Neural Network Optimization&#xff09;是由Intel推出的&#xff0c;用于加速深度学习模型推理的工具套件。它旨在提高计算机视觉和深度学…...

力扣刷MySQL-第五弹(详细讲解)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;力扣刷题讲解-MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出…...

用C语言实现简单的三子棋游戏

目录 1 -> 模块简介 2 -> test.c 3 -> game.c 4 -> game.h 1 -> 模块简介 test.c:测试游戏逻辑 game.c: 函数的实现 game.h:函数的声明 2 -> test.c #define _CRT_SECURE_NO_WARNINGS 1#include "game.h";void menu() {printf("****…...

Yaklang 中的类型和变量

Yaklang 的类型其实非常简单&#xff0c;我们仅需要记住如下类型即可 string 字符串类型&#xff0c;用以快速构建一个字符串int 整数类型&#xff1a;在 64 位机中&#xff0c;int 和 int64 是一样的float 浮点类型&#xff0c;用来定义和表示浮点数byte 本质上等同于 uint8u…...

C语言从入门到实战——编译和链接

编译和链接 前言一、 翻译环境和运行环境二、 翻译环境2.1 预处理&#xff08;预编译&#xff09;2.2 编译2.2.1 词法分析2.2.2 语法分析2.2.3 语义分析 2.3 汇编2.4 链接 三、 运行环境 前言 在C语言中&#xff0c;编译和链接是将源代码转换为可执行文件的两个主要步骤。 编…...

【实战教程】ThinkPHP6分页功能轻松实现,让你的网站更高效!

ThinkPHP是一款非常流行的PHP开发框架&#xff0c;其最新版本ThinkPHP6在性能和易用性方面都得到了很大的改善。分页功能是网页开发中非常常见的功能&#xff0c;而ThinkPHP6也提供了非常方便的分页方法。本文将介绍如何实现ThinkPHP6的分页功能。 一、了解分页功能 在Web应用…...

专业130+总分380+哈尔滨工程大学810信号与系统考研经验水声电子信息与通信

今年专业课810信号与系统130&#xff0c;总分380顺利考上哈尔滨工程大学&#xff0c;一年的努力终于换来最后的录取&#xff0c;期中复习有得有失&#xff0c;以下总结一下自己的复习经历&#xff0c;希望对大家有帮助&#xff0c;天道酬勤&#xff0c;加油&#xff01;专业课&…...

旅游项目day08

1. 旅游日记&#xff08;游记&#xff09; 后端&#xff1a;实体类&#xff0c;列表&#xff0c;查看&#xff0c;审核 前端&#xff1a;目的地明细中-游记->带范围条件查询&#xff0c;游记首页&#xff0c;【扩展】游记添加/编辑&#xff0c;【扩展】添加游记时间没登录时…...

蓝桥杯真题(Python)每日练Day2

题目 题目分析 对于本题首先确定其数据结构为优先队列&#xff0c;即邮费最小的衣服优先寄&#xff0c;算法符合贪心算法。可以直接使用queue库的PriorityQueue方法实现优先队列。关于PriorityQueue的使用方法主要有&#xff1a; import queue q queue.Queue()# 队列 pq qu…...

IntelliJ IDEA 拉取gitlab项目

一、准备好Gitlab服务器及项目 http://192.168.31.104/root/com.saas.swaggerdemogit 二、打开 IntelliJ IDEA安装插件 打开GitLab上的项目&#xff0c;输入项目地址 http://192.168.31.104/root/com.saas.swaggerdemogit 弹出输入登录用户名密码&#xff0c;完成。 操作Comm…...

RHCSA上课笔记(前半部分)

第一部分 网络服务 第一章 例行性工作 1.单一执行的例行性工作 单一执行的例行性工作&#xff08;就像某一个时间点 的闹钟&#xff09;&#xff1a;仅处理执行一次 1.1 at命令&#xff1a;定时任务信息 [rhellocalhost ~]$ rpm -qa |grep -w at at-spi2-core-2.40.3-1.el9.x…...

C++代码入门05 字符串容器

图源&#xff1a;文心一言 上机题目练习整理&#xff0c;本篇作为字符串容器的代码&#xff0c;提供了常规解法及其详细解释&#xff0c;供小伙伴们参考~&#x1f95d;&#x1f95d; 第1版&#xff1a;在力扣新手村刷题的记录~&#x1f9e9;&#x1f9e9; 方法&#xff1a;常…...

vue3 项目中 arguments 对象获取失败问题

问题 在 vue3 项目中 获取到的 arguments 对象与传入实参不符&#xff0c;打印出函数中的 arguments 对象显示如下&#xff1a; 原因 作者仔细回看代码才发现&#xff0c;自己一直用的是 vue3 的组合式写法&#xff0c;函数都是箭头函数&#xff0c;而箭头函数不存在 argumen…...

12.线程同步

12.线程同步 1. 为什么需要线程同步2. 互斥锁2.1 互斥锁初始化2.1.1 PTHREAD_MUTEX_INITIALIZER 宏初始化2.1.2 使用函数初始化 2.2 加锁和解锁2.3 pthread_mutex_trylock()2.4 销毁互斥锁2.5 互斥锁死锁2.6 互斥锁的属性 3. 条件变量3.1 条件变量初始化3.2 通知和等待条件变量…...

开发安全之:System Information Leak: External

Overview 在调用 error_reporting() 过程中&#xff0c;程序可能会显示系统数据或调试信息。由 error_reporting() 揭示的信息有助于攻击者制定攻击计划。 Details 当系统数据或调试信息通过套接字或网络连接使程序流向远程机器时&#xff0c;就会发生外部信息泄露。 示例 1…...

burp靶场--文件上传

burp靶场–文件上传 https://portswigger.net/web-security/file-upload/lab-file-upload-remote-code-execution-via-web-shell-upload 1.文件上传 1、原理&#xff1a;文件上传漏洞是指Web服务器允许用户将文件上传到其文件系统&#xff0c;而不充分验证文件的名称、类型、…...

mac 中vscode设置root启动

1. 找到你的vscode app&#xff0c;点击鼠标右键------->选项----->在访达中显示 2. 终端中输入以下命令&#xff0c;不要点回车&#xff0c;不要点回车&#xff0c;输入一个空格 sudo chflags uchg 3. 然后将你的程序拖到终端&#xff0c;会自动…...

【MySQL数据库专项 一】一个例子讲清楚数据库三范式

好的&#xff0c;让我们以学校数据库中的一个表为例来说明第一范式&#xff08;1NF&#xff09;、第二范式&#xff08;2NF&#xff09;和第三范式&#xff08;3NF&#xff09;的概念。 什么是数据库三范式 数据库的范式&#xff08;Normalization&#xff09;是一组关于数据…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

接口自动化测试:HttpRunner基础

相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具&#xff0c;支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议&#xff0c;涵盖接口测试、性能测试、数字体验监测等测试类型…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...