一个可以等待的异步计数器

NoP*_*God 6 c# asynchronous task-parallel-library async-await

我有一个连接类,它有几个异步方法,如SendText,SendImage等.

连接类有一个Disconnect方法,当它被调用时,我必须注意不要在所有异步方法完成执行之前开始更改类的内部状态.

我相信实现这一目标的一个好方法是简单地保持执行中的操作数量的总计,然后当我想断开连接时我可以简单地设置Disconnecting = true然后等待计数达到0

我正在考虑这方面的事情

class ReferenceCounter
{
    void Increment();

    void Decrement();

    async Task WaitForCounterToReachZero();
}
Run Code Online (Sandbox Code Playgroud)

然后,当异步操作开始时,我可以做到

refCounter.Increment();
Run Code Online (Sandbox Code Playgroud)

当它结束

refCounter.Decrement();
Run Code Online (Sandbox Code Playgroud)

并在Disconnect方法内部

disconnecting = true;
taskCancellationSource.Cancel();
await refCounter.WaitForCounterToReachZero();
Cleanup();
Run Code Online (Sandbox Code Playgroud)

是否有像这样的内置.NET类?

或者对我来说更重要的是,有更好的方法吗?

如果它是同步代码,那就简单了

lock (thisLock)
{
    while (counter > 0)
        Monitor.Wait(thisLock);
}
Run Code Online (Sandbox Code Playgroud)

我刚刚发现内置的CountdownEvent类做了同样的事情,但它没有异步Wait方法,也没有任何事件,所以我必须阻止.

Jon*_*eet 5

好吧,假设将其设为0 后再也不会增加,则可以执行以下操作:

public class Latch
{
    private int count = 0;
    private readonly TaskCompletionSource<object> tcs =
        new TaskCompletionSource<object>();

    public void Increment()
    {
        Interlocked.Increment(ref count);
    }

    public void Decrement()
    {
        if (Interlocked.Decrement(ref count) == 0)
        {
            tcs.TrySetValue(null);
        }
    }

    public Task Task { get { return tcs.Task; } }
}
Run Code Online (Sandbox Code Playgroud)

然后您可以等待someLatch.Task。或者,您可以使闩锁本身可以等待:

public TaskAwaiter GetAwaiter()
{
    return tcs.Task.GetAwaiter();
}
Run Code Online (Sandbox Code Playgroud)

您可能应该考虑如何防止“计数下降到0后计数增加”方面-考虑您要执行的操作。(在上面的代码中,一旦设置了TCS的值,将立即完成进一步的等待。)

  • 仅供参考,Stephen Toub在他的博客上有一个[`AsyncCountdownEvent`](http://blogs.msdn.com/b/pfxteam/archive/2012/02/11/10266930.aspx),它也是[AsyncEx的一部分库](http://nitoasyncex.codeplex.com/wikipage?title=AsyncCountdownEvent)。 (4认同)
  • @NoPyGod:不清楚您是否真的需要计数-您谈论的是大量异步方法...它们都返回`Task`还是类似的东西?如果是这样,`Task.WhenAll`是您的朋友。您可以保留大量未完成的任务,只需等待Task.WhenAll来等待它们全部完成。 (2认同)