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

这篇具有很好参考价值的文章主要介绍了C# 实现单线程异步互斥锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

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

可能出现的组合,效果预览
c#实现线程都要经过一个阻塞的方法,线程之间互不干涉,.Net,c#,wpf,异步编程,mutex,互斥锁

(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#实现线程都要经过一个阻塞的方法,线程之间互不干涉,.Net,c#,wpf,异步编程,mutex,互斥锁


总结

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

到了这里,关于C# 实现单线程异步互斥锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 观察者模式(下):如何实现一个异步非阻塞的EventBus框架?

    上一节课中,我们学习了观察者模式的原理、实现、应用场景,重点介绍了不同应用场景下,几种不同的实现方式,包括:同步阻塞、异步非阻塞、进程内、进程间的实现方式。 同步阻塞是最经典的实现方式,主要是为了代码解耦;异步非阻塞除了能实现代码解耦之外,还能

    2024年02月16日
    浏览(37)
  • C#多线程学习(二) 如何操纵一个线程

    线程学习第一篇: C#多线程学习(一) 多线程的相关概念 下面我们就动手来创建一个线程,使用Thread类创建线程时,只需提供线程入口即可。(线程入口使程序知道该让这个线程干什么事) 在C#中,线程入口是通过ThreadStart代理(delegate)来提供的,你可以把ThreadStart理解为一个

    2023年04月19日
    浏览(43)
  • C# BeginInvoke实现异步编程

    C# BeginInvoke实现异步编程-CSDN博客 https://blog.csdn.net/Nire_Yeyu/article/details/133203267   C# BeginInvoke实现异步编程 BeginInvoke实现异步编程的三种模式: 1.等待模式 在发起了异步方法以及做了一些其他处理之后,原始线程就中断并且等异步方法完成之后再继续; eg: using System; using Sys

    2024年02月08日
    浏览(43)
  • C#异步有多少种实现方式?

    微信群里的一个提问引发的这个问题,有同学问:C#异步有多少种实现方式?想要知道C#异步有多少种实现方式,首先我们要知道.NET提供的执行异步操作的三种模式,然后再去了解C#异步实现的方式。 .NET 提供了执行异步操作的三种模式: 基于任务的异步模式 (TAP) ,该模式使

    2024年02月03日
    浏览(35)
  • C# BeginInvoke 加 EndInvoke实现异步操作

    1、 定义一个委托 2、 需异步操作的函数 3、 回调方法,在异步方法执行完后才执行 ,接收返回结果 4、 异步调用:

    2024年02月13日
    浏览(31)
  • C# Socket通信从入门到精通(14)——多个异步UDP客户端C#代码实现

    在之前的文章C# Socket通信从入门到精通(13)——单个异步UDP客户端C#代码实现我介绍了单个异步Udp客户端的c#代码实现,但是有的时候,我们需要连接多个服务器,并且对于每个服务器,我们都有一些比如异步发送、异步接收的操作,那么这时候我们使用之前单个异步Udp客户

    2024年02月03日
    浏览(81)
  • C#编程-实现多线程

    多线程帮助同时执行各种操作。这为用户节省时间。多线程程序包括一个主线程和其他用户定义的线程以同时执行多个任务。 微处理器为执行的进程分配内存。每个进程占有内存中它们自己的地址空间。但是,所有在进程中的线程占有相同的地址空间。多线程允许在一个程序

    2024年02月02日
    浏览(44)
  • C#实现一个简单的日志类

    目录 自定义日志类 NLog版本的日志类 Serilog版本的日志类 上个月换工作,新项目又要重新搭建基础框架,把日志实现部分单独记录下来方便以后参考。 代码大部分使用ChatGPT生成,人工进行了测试和优化,主要特点: 线程安全,日志异步写入文件不影响业务逻辑 支持过期文件

    2024年02月10日
    浏览(41)
  • 协程是怎么实现的,如何用纯C#怎么实现协程,携程和线程的区别

    协程(Coroutine)是一种特殊的函数,可以暂停执行并在稍后恢复执行。在Unity中,协程通常用于实现一些异步操作,如等待一段时间后再执行某个操作,或者分步执行一个复杂的操作。 在Unity中,协程可以使用C#的yield来实现。yield语句可以暂停协程的执行,并在稍后恢

    2024年02月04日
    浏览(48)
  • C#上位机与三菱FX3UPLC实现异步伪实时串口通信机制(串口类通信可参考)

    串口是串行接口(serial port)的简称,也称为 串行通信接口 或 COM接口。 串口通信(serial communication)是指采用串行通信协议在一条信号线上将数据一个比特一个比特地逐位进行传输的通信模式。 串口按电气标准及协议来划分,包括RS-232-C、RS-422、RS485等。 在串行通信中,数

    2023年04月21日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包