TaskCompletionSource生成的任务需要Dispose()吗?

5
我在我的软件中使用TaskCompletionSource来分发网络数据包给async/await方法。因此,在我的代码中,有许多点需要等待从接收处理程序中分离出并传递给方法async Wait()的网络数据包。每秒可能会有很多、很多个数据包,我要决定是将数据包推送到TaskCompletionSource还是放入Queue中。所以,每当没有TaskCompletionSource时,我都会创建一个新的,这会导致一个新的Task对象。
根据这个问题Do I need to dispose of a Task?和这篇博客Parallel Programming with .NET的说法,Task不需要被Dispose。然而,我有时会每秒实例化几千个TaskCompletionSource。链接博客中的详细答案还说,Task可能在内部使用WaitHandle。现在我强烈感觉到,我的情况恰恰是应该对来自TaskCompletionSource的任务使用Dispose的情况。
这是我等待新数据包的方法。这个方法将使用await调用,并且也可以在并行中被频繁地调用:
public async Task<Packet> Wait()
{
    Packet packet;

    lock (sync)
        if (packets.TryDequeue(out packet))
            return packet;
        else
            waiter = new TaskCompletionSource<Packet>();

    return await waiter.Task;
}

我在网络处理程序中推送数据包的方法如下:

public void Poke(Packet packet)
{
    lock (sync)
        if (waiter == null)
            packets.Enqueue(packet);
        else
            waiter.SetResult(packet);
}

我正在使用.NET Core >= 2.2。该博客文章指出,WaitHandle的行为也在.NET 4.5中发生了变化。
问题:在这种特定情况下,我需要释放Task吗?如果在此代码路径上传递许多数据包时不释放由TaskCompletionSource创建的任务,那么我会创建许多句柄吗?这是博客文章警告我的场景吗?
请勿告诉我,只要您不能告诉我一种非常兼容async/await模式并能够将这些数据包分配给各个选定侦听器的更好方法,就认为此方法是一个糟糕的选择。也请不要告诉我,由于许多网络数据包而创建许多对象通常是一个坏主意。
1个回答

7

这是博客文章警告我的情况吗?

"我需要处理这个 Task 吗?" 这个问题只能根据任务的使用方式来回答。特别是,请考虑以下摘自博客文章的引用:

只有在显式请求任务的 IAsyncResult.AsyncWaitHandle 时,WaitHandle 才会被分配,而这应该非常罕见。

Reed's answer 在当时是正确的,但现在继续使用 AsyncWaitHandle 的情况已经不存在了。现在,没有任何东西会隐式地使用 AsyncWaitHandle,所以您只应该在将任务视为 IAsyncResult 并访问 AsyncWaitHandle 属性的情况下才考虑 Disposeing Tasks。即使是在这种情况下,您也只应该考虑将它们处理掉;这不是严格必要的。

只要你不能告诉我一个非常兼容异步/等待模式并且能够将这些数据包分发给各个选定的侦听器的更好方法,就请不要告诉我这种方法是一个糟糕的选择。

我建议构建一个与异步兼容的生产者/消费者队列作为单独的类型; 我认为这将有助于使您的代码更清晰。 您可以 使用 BufferBlock<T>, 也可以 使用我的AsyncEx库中的异步队列类型, 或者使用与异步兼容的监视器构建自己的队列。

此外,如果您经常期望您的 Wait() 方法已经有一个可用的数据包,则考虑使用 ValueTask<T>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接