using System;
using System.Threading;
using System.Threading.Tasks;

namespace XiaoZhiSharp.Kernels
{
    /*使用示例：
     private static AsyncSemaphore s_asyncSemaphore = new AsyncSemaphore();
     Task.Run(async () =>
     {
         using (await s_asyncSemaphore.WaitAsync())
         {
             //await ......
         }
     });
     */
    /// <summary>
    /// C# 在await中使用Lock出现cannot await in the body of a lock statement解决方法
    /// 如果试图在 lock 块中使用 async 关键字时使用 lock 关键字，
    /// 会得到这个编译错误：cannot await in the body of a lock statement。
    /// 原因是在async 完成之后，该方法可能会在一个不同的线程中运行，而不是在async 关键字之前。
    /// lock 关键字需要同一个线程中获取锁和释放锁。
    /// 如何解决这个问题？
    /// 不能为此使用 Monitor，因为 Monitor 需要从它获取锁的同一线程中释放锁。lock 关键字基于 Monitor。
    /// 虽然 Mutex 对象可以用于不同进程之间的同步，但它有相同的问题：它为线程授予了一个锁。
    /// 从不同的线程中释放锁是不可能的。
    /// 相反，可以使用 Semaphore 或 SemaphoreSlim 类。
    /// Semaphore 可以从不同的线程中释放信号量。
    /// https://blog.csdn.net/weixin_67336587/article/details/125219994
    /// </summary>
    public sealed class AsyncSemaphore
    {
        private class SemaphoreReleaser : IDisposable
        {
            private SemaphoreSlim _semaphore;

            public SemaphoreReleaser(SemaphoreSlim semaphore) =>
              _semaphore = semaphore;

            public void Dispose() => _semaphore.Release();
        }

        private SemaphoreSlim _semaphore;
        public AsyncSemaphore() => _semaphore = new SemaphoreSlim(1);

        public async Task<IDisposable> WaitAsync()
        {
            await _semaphore.WaitAsync();
            return new SemaphoreReleaser(_semaphore) as IDisposable;
        }
    }
}
