C# 实现单线程异步互斥锁_c#异步线程互锁执行-程序员宅基地

技术标签: c#  .Net  mutex  异步编程  wpf  互斥锁  


前言

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

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


总结

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u013113678/article/details/135641028

智能推荐

SQLServer中文处理(一)_sqlserver nums-程序员宅基地

文章浏览阅读423次。按:只要接触了电脑,乱码问题总会遇到过。这是一个让人恼火的问题。如果对字符编码一知半解,乱码就仿佛一种神秘咒语,似乎一不小心就触怒了电脑爷,扔出一堆天书般的乱码来;而如果深入理解了字符编码,各种编码在你看来就会不一不异,而一切乱码问题都不过是浮云。本文不是专门介绍字符编码的文章,只是谈一下与SQLServer中文处理相关的字符编码和排序规则,希望对各位SQLServer玩家有所帮助。首_sqlserver nums

利用python实现根据excle表格数据自动生成折线图代码_python根据excel画折线图-程序员宅基地

文章浏览阅读2.8k次,点赞2次,收藏15次。利用python实现根据excle表格数据自动生成折线图代码_python根据excel画折线图

Netty学习笔记(四)--- ChannelHandler_netty 自定义了hander 通过实现channelread0-程序员宅基地

文章浏览阅读679次,点赞2次,收藏2次。在前面的文章中,通过对引导和线程的介绍,我们知道了如何配置、引导客户端和服务端程序,以及程序的线程模型。它们构建了应用程序运行的框架,但一个完整的网络应用程序还需要更多的内容,其中包括数据应该如何处理。Netty为此提供了一个强大功能组件ChannelHanlder接口,它允许用户自定义ChannelHandler的实现来处理传入和传出的数据。在一款网络应用程序中,数据应该是其中最重要的部分,其..._netty 自定义了hander 通过实现channelread0

Nachos线程管理_nachos中第一个线程是如何产生的?-程序员宅基地

文章浏览阅读7.8k次,点赞9次,收藏18次。Nachos线程管理 Nachos中的线程是在内核中以一个thread类的对象的方式实现的。线程控制块是以类的数据成员的方式实现。 //thread类源代码--定义class Thread { private: // NOTE: DO NOT CHANGE the order of these first two members._nachos中第一个线程是如何产生的?

IDEA 报错:LifecycleException: Failed to start component_idea 启动 lifecleexception: failed to start compoent-程序员宅基地

文章浏览阅读810次。报错信息:严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start:org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].Standar.._idea 启动 lifecleexception: failed to start compoent [standardengine]

JS基础-密码框验证信息案例_js密码框验证信息-程序员宅基地

文章浏览阅读616次。需要实现的效果当输入密码的位数小于6位或者大于16位的时候,展现下面的效果:当输入的密码在正确范围的时候,展现下面的效果:三个图标给出,可自行点击下载:代码实现如下:<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=ed..._js密码框验证信息

随便推点

8个高质量免抠素材网站-程序员宅基地

文章浏览阅读6.3k次。8个高质量免抠素材网站 标签:免扣素材ppt 素材1.PngImg网址: http://pngimg.com/PngImg 网站是一个收录了近5万个免费的网页设计图片素材的站点,拥有详细的分类,如蔬菜、动物、水果、花卉、服装、食品、家具等等,所有的素材资源设计师们都可以无需注册免费下载使用。PngImg 网站内建搜寻功能,或者可使用单字开头AZ方式快速查找,本身首页设计就跟一般的..._免扣素材库

常见三维表示方法_常见三维空间表达方式-程序员宅基地

文章浏览阅读7.1k次,点赞26次,收藏80次。三维表示是机器视觉的一项关键技术,它能直观的反映物体的形状,与我们熟悉的二维表示相比,三维表示带有深度信息,因此有效的三维表示是实现三维模型重建、三维目标检测、场景语义分割等机器视觉任务的重要关键,在机器人、AR/VR、人机交互、遥感测绘等领域有着广泛的应用前景。_常见三维空间表达方式

Linux下最强安卓模拟器,流畅又丝滑(附详细安装教程)此瓜保熟|Linux游戏党_linux安卓模拟器-程序员宅基地

文章浏览阅读3.7w次,点赞16次,收藏73次。Linux下最强安卓模拟器!各项体验堪称完美,安装简便,不会卡顿!_linux安卓模拟器

Python笔记(二十八)_魔法方法_迭代器-程序员宅基地

文章浏览阅读82次。迭代器用于遍历容器中的数据,但它不是容器,它是一个实现了__next__方法的对象与迭代器相关的内置函数:iter(): 将一个对象转换成一个迭代器 next(): 访问迭代器中的下一个变量,直到无可用变量,则抛出StopIteration异常>>> string='demi'>>> name=iter(string)>>..._self.closed = true try: next(self.predictions)

计算机网络:数据链路层概述_数据链路层在进行帧传输时可以对接收到的数据帧进行差错检测,ethermet ii类型-程序员宅基地

文章浏览阅读2.3k次。视频内容学习自B站:湖科大教书匠-计算机网络微课堂。 可以自己去搜着学习,你们关注我关注的我心慌。。。。。。这里只是我自己学习的笔记,如果对你们有帮助的话我还是很开心的。------------------------------------------------------------------------------------------------------------------------------------------------------------/..._数据链路层在进行帧传输时可以对接收到的数据帧进行差错检测,ethermet ii类型

Hadoop2.6.4  重新编译 64  位本地库_重新编译库文件生成64位库文件-程序员宅基地

文章浏览阅读1.2w次。Hadoop2.6.4 重新编译 64 位本地库解释在前:Hadoop只提供32位编译好的本地库,不提供64位编译好的本地库版本,我们需要用hadoop2.7.2源码进行64位本地库编译。环境:虚拟机 VMware Pro,操作系统 64 位 CentOS 6.7下载重新编译需要的软件包资源整合:https://download.csdn.net/download/as1072966956/1..._重新编译库文件生成64位库文件

推荐文章

热门文章

相关标签