如果我使用ValueTask实现实际的异步,返回TaskCompletionSource还有好处吗?
据我所知,ValueTask的目的是减少分配,但在等待TaskCompletionSource.Task时,分配仍然存在。下面是一个简单的例子来说明这个问题:
async ValueTask DispatcherTimerDelayAsync(TimeSpan delay)
{
var tcs = new TaskCompletionSource<bool>();
var timer = new DispatcherTimer();
timer.Interval = delay;
timer.Tick += (s, e) => tcs.SetResult(true);
timer.Start();
try
{
await tcs.Task;
}
finally
{
timer.Stop();
}
}我是否应该从Task (相对于ValueTask)返回DispatcherTimerDelayAsync,它本身总是被期望是异步的?
发布于 2020-07-08 06:26:27
每种方法各有优缺点。在“专业”一栏中:
Task<T>)时,使用ValueTask<T>可以避免任务的分配--但是,这个不会将应用于"void“(即非泛型Task),因为您可以只返回Task.CompletedTask。( "2“的特例可能是通过像PooledAwait这样的工具摊销)
这两种方法都不适用于此,但是:如果有疑问,请记住,将Task作为ValueTask (return new ValueTask(task);)返回是免费的,这将允许您考虑稍后将实现更改为无分配的实现,而不会破坏签名。当然,您仍然需要支付原始Task等的费用--但将它们作为ValueTask公开并不会增加任何额外费用。
老实说,在这种情况下,我不确定我是否会担心太多,因为拖延是分配的方式。
发布于 2020-07-08 06:13:34
ValueTask是一个结构,而Task是一个类,这就是它可能减少分配的原因。(struct大部分时间都在堆栈上分配,所以当方法退出时,它们会自动消失(退出strackframe)。classes主要在堆上分配,在堆中必须执行GC 收集或扫描来收集不可访问的对象。因此,如果可以在堆栈上分配,那么堆内存分配就不会增加(因为这个对象),这就是GC没有被触发的原因。
当您的代码主要同步执行时,可以看到ValueTask的主要好处。这意味着,如果期望值已经存在,那么就没有必要创建一个承诺(TaskCompletionSource),它将在未来实现。
您也不需要担心在Task<bool>情况下的分配,因为在这两种情况下,相应的任务对象都是由运行时本身缓存的。同样的情况也适用于Task<int>,但仅适用于-1到9之间的数字。
因此,总之,在这种情况下,您应该使用Task而不是ValueTask。
https://stackoverflow.com/questions/62788515
复制相似问题