C#实现单线程异步互斥锁的示例代码

 更新时间:2024年01月17日 14:20:39   作者:CodeOfCC  
异步互斥锁的作用是用于确保存在异步操作的上下文同步互斥,这篇文章主要为大家详细介绍了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);

可能出现的组合,效果预览

(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#实现单线程异步互斥锁的示例代码的详细内容,更多关于C#单线程异步互斥锁的资料请关注脚本之家其它相关文章!

相关文章

  • C# Console.WriteLine()用法案例详解

    C# Console.WriteLine()用法案例详解

    这篇文章主要介绍了C# Console.WriteLine()用法案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C# DI依赖注入的实现示例

    C# DI依赖注入的实现示例

    依赖注入是一种实现的方法,用于减少代码之间的耦合,本文主要介绍了C# DI依赖注入的实现示例,具有一定的参考价值,感兴趣可以了解一下
    2023-12-12
  • C#操作mysql数据库的代码实例

    C#操作mysql数据库的代码实例

    这篇文章为大家提供了一个C#操作mysql数据库的实例,大家可以参考使用
    2013-11-11
  • C# WebApi Get请求方式传递实体参数的方法示例

    C# WebApi Get请求方式传递实体参数的方法示例

    这篇文章主要给大家介绍了关于C# WebApi Get请求方式传递实体参数的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04
  • 用C#在本地创建一个Windows帐户(DOS命令)

    用C#在本地创建一个Windows帐户(DOS命令)

    用C#在本地创建一个Windows帐户(DOS命令)...
    2007-03-03
  • C#中加载dll并调用其函数的实现方法

    C#中加载dll并调用其函数的实现方法

    下面小编就为大家带来一篇C#中加载dll并调用其函数的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • C#不提升自己程序的权限实现操作注册表

    C#不提升自己程序的权限实现操作注册表

    这篇文章主要介绍了C#不提升自己程序的权限实现操作注册表的相关资料,需要的朋友可以参考下
    2022-12-12
  • 基于C#解决库存扣减及订单创建时防止并发死锁的问题

    基于C#解决库存扣减及订单创建时防止并发死锁的问题

    这篇文章主要介绍了基于C#解决库存扣减及订单创建时防止并发死锁的问题,很多开发人员对于这个问题的排查起来是比较困难的,而生产生的原因多种多样,很多人认是因为表中的数据太多了同时操作的人多人才会产生这种错误,下面我们来还原一下死锁的过程
    2022-05-05
  • C#中DrawCurve的用法小结

    C#中DrawCurve的用法小结

    本文主要介绍了C#中DrawCurve的用法小结,通常用于绘制一条平滑的曲线通过一系列给定的点,具有一定的参考价值,感兴趣的可以了解一下
    2025-04-04
  • C#操作注册表之RegistryKey类

    C#操作注册表之RegistryKey类

    这篇文章介绍了C#操作注册表之RegistryKey类,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05

最新评论