使用Task.FromAsync的IAsyncResult重载会带来性能成本

7
我正在尝试将现有的APM调用(BeginXEndX)包装在Tasks中,以获得所有良好的优势。不幸的是,我们的方法是非常规的,并使用out参数,因此无法使用标准的FromAsync方法,其中您提供开始和结束委托并让它很好地包装。 这篇文章描述了另一种选择:一个重载,它接受一个IAsyncResult并且只需要你实现结束回调函数。他们获取IAsyncResult句柄,然后等待它完成,然后调用您传递的委托。
这似乎还好,但是我读了另一篇关于在任务中包装APM调用的文章。他还提到IAsyncResult重载不如其他方法高效。在我看来,这意味着回调函数没有被用来报告方法的完成。这意味着他们必须使用AsyncWaitHandle或轮询IsCompleted。他们使用哪一个?会有多大的性能损失?
如果它正在进行轮询,那么意味着回调可能不会立即到来,他们必须在整个调用期间忙碌地检查它。如果他们拥有一个AsyncWaitHandle,则有另一个线程坐在那里等待结果,这完全违背了我使用异步方法的初衷。
有人知道他们在做什么以及这种性能损失有多严重吗?
4个回答

1

在JustDecompile中查看代码,它创建了一个包装IAsyncObject的TaskCompletionSource,创建了一个新的任务,这可能意味着一个线程正在等待IAsyncObject完成。虽然并不理想,但这可能是唯一的方法,因为没有办法制定适用于所有情况的良好轮询率。


1
有人知道他们在做什么以及这种性能惩罚有多严重吗?
它并不是非常严重,但有更多的开销。由于您没有提供相同的信息(只有 IAsyncResult),因此实现必须调用 ThreadPool.RegisterWaitForSingleObject 来触发回调,当 IAsyncResult 完成时。
当不使用此方法,并且存在 Begin/End 对 + 回调时,回调可以自动触发任务的完成,从而消除了此处的额外等待调用。这实际上将一个 ThreadPool 线程绑定到等待句柄上,直到操作完成为止。如果这是一个罕见的情况,性能开销可能是可以忽略的,但如果您经常这样做,可能会出现问题。

谢谢提供的信息。我正在努力将一个系统转换为异步,以解决在阻塞调用上线程池绑定吞吐量问题。我猜现在我没有使用任务来进行这些调用。 - RandomEngy

0
当你传入一个ISyncResult时,TaskFactory没有办法插入回调函数。因此,TaskFactory需要使用ISynResult启动一个WaitHandle,并需要ThreadPool获取一个线程来等待此句柄。这里没有涉及轮询。

0

我来到这里是因为我遇到了类似的问题(涉及out参数),但只出现在End方法中。这是我想到的解决方案,希望对某些人有所帮助:

var provider = new Provider();
return Task<Whatever>.Factory.FromAsync(provider.Begin, ar =>
{
    Whatever outparam;
    provider.End(out outparam);
    return outparam;
}, state);

编译成功 :)


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