在 .NET 4 中使用 await SemaphoreSlim.WaitAsync

Using await SemaphoreSlim.WaitAsync in .NET 4

我的应用程序正在使用 .NET 4。我正在使用 nuget package

等待异步

在我的应用程序中,我想对 sempahore WaitAsync 调用进行如下等待。

SemaphoreSlim semphore = new SemaphoreSlim(100);
await semphore.WaitAsync();

但是我遇到以下编译错误。

'System.Threading.SemaphoreSlim' does not contain a definition for 'WaitAsync' and no extension method 'WaitAsync' accepting a first argument of type 'System.Threading.SemaphoreSlim' could be found (are you missing a using directive or an assembly reference?)

.NET 4.0 中是否有 uisng WaitAsync?

不,您需要升级到 .NET 4.5(或自己编写 WaitAsync 扩展(或固有的异步信号量))。

.NET 4.0 的异步扩展可以支持实际的 await 关键字。 .NET 4.5 的大部分工作是向可以等待的各种 BCL 类型添加异步操作。如果您需要这些操作,您需要升级您使用的框架版本。

WaitAsync 是在 .Net 4.5 中引入的。您要么需要通过查看 source(not sure if that is possible), or you can use StephenToub's AsyncSemaphore.

将自己实现为扩展方法

您不能在 .Net 4.0 中使用 SemaphoreSlim.WaitAsync,因为此方法已添加到 .Net 4.5 中的 SemaphoreSlim

但是,您可以按照 Stephen Toub 在 Building Async Coordination Primitives, Part 5: AsyncSemaphore 中的示例实施自己的 AsyncSemaphore

public class AsyncSemaphore
{
    private readonly static Task s_completed = Task.FromResult(true);
    private readonly Queue<TaskCompletionSource<bool>> m_waiters = new Queue<TaskCompletionSource<bool>>();
    private int m_currentCount; 

    public AsyncSemaphore(int initialCount)
    {
        if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount");
        m_currentCount = initialCount;
    }

    public Task WaitAsync()
    {
        lock (m_waiters)
        {
            if (m_currentCount > 0)
            {
                --m_currentCount;
                return s_completed;
            }
            else
            {
                var waiter = new TaskCompletionSource<bool>();
                m_waiters.Enqueue(waiter);
                return waiter.Task;
            }
        }
    }
    public void Release()
    {
        TaskCompletionSource<bool> toRelease = null;
        lock (m_waiters)
        {
            if (m_waiters.Count > 0)
                toRelease = m_waiters.Dequeue();
            else
                ++m_currentCount;
        }
        if (toRelease != null)
            toRelease.SetResult(true);
    }
}

由于 WaitAsync 在 .NET 4.0 中不可用,您可以使用 Stephan Toub's series of async synchronization primitives:

中的代码
public class AsyncSemaphore 
{ 
    private readonly static Task s_completed = Task.FromResult(true); 
    private readonly Queue<TaskCompletionSource<bool>> m_waiters = 
                                            new Queue<TaskCompletionSource<bool>>(); 
    private int m_currentCount; 

    public AsyncSemaphore(int initialCount)
    {
        if (initialCount < 0) 
        {
            throw new ArgumentOutOfRangeException("initialCount"); 
        }
        m_currentCount = initialCount; 
    }

    public Task WaitAsync() 
    { 
        lock (m_waiters) 
        { 
            if (m_currentCount > 0) 
            { 
                --m_currentCount; 
                return s_completed; 
            } 
            else 
            { 
                var waiter = new TaskCompletionSource<bool>(); 
                m_waiters.Enqueue(waiter); 
                return waiter.Task; 
            } 
        } 
    }

    public void Release() 
    { 
        TaskCompletionSource<bool> toRelease = null; 
        lock (m_waiters) 
        { 
            if (m_waiters.Count > 0) 
                toRelease = m_waiters.Dequeue(); 
            else 
                ++m_currentCount; 
        } 
        if (toRelease != null) 
            toRelease.SetResult(true); 
    }
}

正如其他人所提到的,.NET 4.5 引入了 SemaphoreSlim.WaitAsync

对于 .NET 4.0,您可以 write your own async-compatible lock or use one from my Nito.AsyncEx NuGet package.

另外两个答案中发布的 class AysncSemaphore 无法使用 Framework 4.0 进行编译,因为 Task.FromResult 未定义。

这是我的替代版本:

public class AsyncSemaphore
{
    private readonly static Task s_completed ;
    private readonly Queue<TaskCompletionSource<bool>> m_waiters = new Queue<TaskCompletionSource<bool>>();
    private int m_currentCount; 

    static AsyncSemaphore()
    {
      var tcs = new TaskCompletionSource<bool>();
      tcs.SetResult(true);
      s_completed = tcs.Task ;
    }

    public AsyncSemaphore(int initialCount)
    {
        if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount");
        m_currentCount = initialCount;
    }

    public Task WaitAsync()
    {
        lock (m_waiters)
        {
            if (m_currentCount > 0)
            {
                --m_currentCount;
                return s_completed;
            }
            else
            {
                var waiter = new TaskCompletionSource<bool>();
                m_waiters.Enqueue(waiter);
                return waiter.Task;
            }
        }
    }
    public void Release()
    {
        TaskCompletionSource<bool> toRelease = null;
        lock (m_waiters)
        {
            if (m_waiters.Count > 0)
                toRelease = m_waiters.Dequeue();
            else
                ++m_currentCount;
        }
        if (toRelease != null)
            toRelease.SetResult(true);
    }
}