首页 > 其他分享 >lock与SemaphoreSlim的区别与应用

lock与SemaphoreSlim的区别与应用

时间:2024-05-29 10:57:11浏览次数:26  
标签:异步 SemaphoreSlim 区别 lock private 线程 public

在多线程编程中,线程同步是确保数据一致性和避免竞争条件的重要手段。本文将深入探讨 lock(object)SemaphoreSlim 这两种常用的同步机制,详细分析它们的区别、适用场景以及如何在实际开发中选择合适的同步工具。

一、lock(object)(或 Monitor

1. 单线程访问: lock 关键字用于确保在同一时间只有一个线程能够访问被保护的代码块。它实现了互斥锁(mutex),适用于单线程临界区的保护。这意味着当一个线程进入锁定区域时,其他试图进入该区域的线程将被阻塞,直到锁被释放。

2. 锁范围: lock 保护的代码块只能在单个线程内运行,进入锁定区域的线程会被阻塞直到锁被释放。它提供了一种简单而有效的方式来防止多个线程同时访问共享资源。

3. 实现方式: lock 是基于 Monitor 类的,它是CLR提供的一种基础同步机制。Monitor 提供了 EnterExit 方法来显式地进入和离开临界区,lock 关键字在语法上对其进行了简化,使得代码更加易读。

4. 简单易用: lock 语法简洁,易于使用,适用于简单的线程同步需求。典型的用法如下:

private static readonly object _lockObject = new object();

public void SomeMethod()
{
    lock (_lockObject)
    {
        // Critical section.
    }
}

5. 不支持异步: lock 不能用于异步代码块,不能与 await 一起使用。这意味着在需要异步处理的场景中,lock 并不适用。

二、SemaphoreSlim

1. 多线程访问: SemaphoreSlim 允许指定同时可以访问资源的线程数。它可以用作计数信号量,允许多个线程并发访问指定数量的资源。例如,初始化为1的 SemaphoreSlim 等价于一个互斥锁,而初始化为大于1的 SemaphoreSlim 则允许指定数量的线程并发访问。

2. 锁范围: SemaphoreSlim 可以控制同时访问资源的多个线程,适用于需要限制并发访问数量的场景。它在资源访问控制方面提供了更大的灵活性。

3. 实现方式: SemaphoreSlim 是一个轻量级的、基于信号量的同步机制。它支持异步操作,使其在需要控制并发访问的异步编程中尤为适用。

4. 异步支持: SemaphoreSlim 提供了异步等待功能,可以与 asyncawait 关键字一起使用。这使得它非常适用于异步编程模型,能够有效避免异步方法中的阻塞问题。

private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

public async Task SomeMethodAsync()
{
    await _semaphore.WaitAsync();
    try
    {
        // Critical section.
    }
    finally
    {
        _semaphore.Release();
    }
}

5. 复杂使用场景: SemaphoreSlim 适用于更复杂的并发控制需求,如限制并发访问数量或需要异步支持的场景。它能够根据具体的并发需求,灵活调整允许并发访问的线程数量。

6.工作原理:

  1. WaitAsync 方法
    • 当调用 WaitAsync 方法时,如果信号量的计数大于 0,则计数会减 1,并且立即允许调用线程进入临界区。
    • 如果信号量的计数为 0,则调用线程会进入等待状态,直到有其他线程调用 Release 方法增加了计数,或者超时。
  2. Release 方法
    • 调用 Release 方法会增加信号量的计数。如果有线程正在等待进入临界区,则会释放其中一个线程,使其可以进入临界区执行任务。
    • 如果没有等待的线程,则信号量的计数会累加,超过 SemaphoreSlim(int initialCount, int maxCount)其中的maxCount 则会抛出 SemaphoreFullException

三、实际应用:AsyncLoadHelper<TData>

在实际开发中,我们常常需要在异步方法中进行线程同步。下面是一个 AsyncLoadHelper<TData> 类的实现,它通过 SemaphoreSlim 确保数据加载操作的线程安全性,并且支持异步操作。

public class AsyncLoadHelper<TData> : BindableBase, IDisposable
{
    private TData _data;
    private bool _isLoading;
    private Exception _loadingException;
    private readonly Lazy<DelegateCommand> _loadCommand;
    private readonly Func<CancellationToken, Task<TData>> _dataLoadMethod;
    private CancellationTokenSource _cts;
    private readonly SemaphoreSlim _asyncLock = new SemaphoreSlim(1, 1);

    public TData Data
    {
        get => _data;
        set => SetProperty(ref _data, value);
    }

    public bool IsLoading
    {
        get => _isLoading;
        set => SetProperty(ref _isLoading, value);
    }

    public Exception LoadingException
    {
        get => _loadingException;
        set => SetProperty(ref _loadingException, value);
    }

    public DelegateCommand LoadCommand => _loadCommand.Value;

    public AsyncLoadHelper(Func<CancellationToken, Task<TData>> dataLoadMethod)
    {
        _dataLoadMethod = dataLoadMethod;
        _loadCommand = new Lazy<DelegateCommand>(() =>
            new DelegateCommand(async () => await ExecuteLoadDataAsync(), () => !IsLoading).ObservesProperty(() => IsLoading));
    }

    public virtual async Task ExecuteLoadDataAsync()
    {
        if (IsLoading) return;
        await _asyncLock.WaitAsync();

        _cts?.Cancel();
        _cts = new CancellationTokenSource();

        IsLoading = true;
        LoadingException = null;

        try
        {
            Data = await _dataLoadMethod(_cts.Token);
        }
        catch (OperationCanceledException)
        {
            // Handle if needed
        }
        catch (Exception e)
        {
            LoadingException = e;
        }
        finally
        {
            IsLoading = false;
            _asyncLock.Release();
        }
    }

    public void Dispose()
    {
        _cts?.Cancel();
        _cts?.Dispose();
        _asyncLock.Dispose();
    }
}

关键点

  • 防止重复加载:使用 IsLoading 防止在加载过程中多次调用 ExecuteLoadDataAsync

  • 同步机制:通过 await _asyncLock.WaitAsync() 确保在任何时候只有一个线程能够进入临界区。

  • 释放信号量:无论加载操作是否成功,finally 块中的 _asyncLock.Release() 确保了信号量总是被释放,从而不会阻塞后续的加载请求。

  • 简单同步:如果需要简单的、单线程的临界区保护,lock 是更简单和直接的选择。它的语法简洁,易于理解和使用,非常适合基本的线程同步需求。

  • 并发控制和异步支持:如果需要控制同时访问资源的线程数量,或者需要在异步代码中使用,SemaphoreSlim 是更为合适的选择。它不仅支持异步操作,还能灵活地控制并发线程的数量,适用于更复杂的同步场景。

标签:异步,SemaphoreSlim,区别,lock,private,线程,public
From: https://www.cnblogs.com/linxmouse/p/18219719

相关文章

  • 请简述strcpy和memcpy的区别
    请简述strcpy和memcpy的区别用途:strcpy:这是一个专门用于复制字符串的函数。它从源字符串(包括终止的空字符)开始,一直复制到目标字符串,直到遇到源字符串的终止空字符。memcpy:这是一个更通用的内存复制函数。它复制指定数量的字节从源地址到目标地址,而不考虑这些字节是否表示字符......
  • 自旋锁与互斥锁的区别
    编程世界中,锁用来保护一个资源不会因为并发操作而引起冲突,导致数据不准确。常见的锁有互斥锁、读写锁、自旋锁、信号量、分布式锁等等。这里仅仅讨论互斥锁和自旋锁的区别。自旋锁是当资源被占用时,锁逻辑循环判断资源是否可用,而不是把进程挂起,直到资源可用。自旋锁采用的是,循......
  • Servlet中/和/*的区别详解
    Servlet中/和/*的区别详解问题在搭建springmvc项目时,DispatcherServlet配置为/*时welcome-file-list失效了报404异常,配置为/时可以正常访问,下面记录问题排查过程所涉及关于servlet的知识<!--welcome-file-list配置--><welcome-file-list><welcome-fil......
  • strcpy和memcpy的区别
    系统编程strcpy和memcpy的区别strcpy与memcpy都是标准C库函数①char*strcpy(char*dest,constchar*src);strcpy:字符串的复制and也会复制字符串的结束符。不需要指定长度,它遇到被复制字符的串结束符"\0"即结束,容易溢出。②voidmemcpy(void**dest,constvoid**sr......
  • 大端和小端的区别
    大端(BigEndian)和小端(LittleEndian)的区别主要体现在字节序的排列上,特别是在多字节数据的存储和传输过程中。以下是两者的详细区别:一、定义与背景大端(BigEndian):指最高有效位(MSB)存储在低位内存地址,而最低有效位(LSB)存储在高位内存地址的方式。这种方式也被称为网络字节序。小......
  • css中px和em的区别
    px和em是用来表示元素大小的单位,在CSS中经常被使用。px(pixel)是像素单位,表示屏幕上的一个点。它是一个绝对单位,表示一个固定的大小。例如,一个宽度为200px的元素将始终显示为200个像素宽。em是相对单位,根据继承它的父元素的字体大小来计算实际的大小。默认情况下......
  • sleep() 和 wait() 有什么区别?
    sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或noti......
  • Overload和Override的区别。Overloaded的方法是否可以改变返回值类型?
    Overload是重载的意思,Override是覆盖的意思,也就是重写。重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。重写Override表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法......
  • strcpy函数和memcpy函数的区别
    strcpy和memcpy的区别1.复制内容:strcpy:专门用于复制字符串,它会一直复制直到遇到源字符串中的'\0'结束符。这意味着如果源字符串长度超过了目标缓冲区的大小(不包括'\0'),就会发生缓冲区溢出,这是一个常见的安全隐患。memcpy:可以复制任意内容,如字符数组、整型、结构体、类等。它按......
  • 请描述一下 cookies sessionStorage和localstorage区别
    Cookies、sessionStorage和localStorage都是Web浏览器提供的客户端存储机制,但它们之间有一些重要的区别:存储容量:Cookies最大容量约为4KB。sessionStorage和localStorage的容量都约为5MB。有效期:Cookies有明确的过期时间,可以设置为在浏览......