.Net ValueTask结构的个人理解
文章中提到ValueTask与Task的地方,都包含ValueTask泛型版本与Task泛型版本意思
md 编辑器可能我不会用,代码和泛型写法出错
背景
await关键字能修饰的不仅仅是Task,可以是一切符合条件能被编译器识别的类型,包括自己写的类型
例如await task.ConfigureAwait(false);这种写法await修饰的就是ConfigureAwait返回的类型,而不是Task
关于ConfigureAwait可以参考开发人员的博客
但是异步方法的返回类型,一般也就是async关键字后面的方法返回类型,貌似之前的时候只能是Task,我也不太确定,但是现在至少确定多了ValueTask
Task的功能太过于强大,支持WaitAll WaitAny WhenAll WhenAny,并且只要完成,不管因为异常还是取消,其状态不再会改变,并且等待多次注册多个延续任务都不会有问题
然而这种强大的代价就是内部其他隐藏对象的支持,这使得在某些情况下过于浪费资源,
例如,异步方法的返回类型会被立即用await关键字等待并且仅仅等待一次,像这样 await GetOneTask()并且很多情况下方法会同步完成,Task的泛型版本根据泛型参数的不同,也无法缓存全部的已经处于完成状态的Task,只能分配一个新的Task,而Task都会在堆中分配
ValueTask
根据如上所述, ValueTask与ValueTask
比如同步完成的异步方法的返回值,因为ValueTask是值类型所以能够减少堆中的分配
然而 ValueTask的最主要的作用就是包装自定义的等待对象,功能跟Task类似,只不过功能比Task少,并且根据情况内部可以重复使用该对象,也正是因为如此,所以文档中说 ValueTask只能等待一次,或者说直接转换为Task再瞎搞,因为一个等待完成,该内部对象可能已经被重置状态复用了
然而假如要自己使用ValueTask的最重要的功能,也就是自定义的等待对象,需要自己实现
庆幸的是类库公开了一个简单的内部类型的简单实现,也就是ManualResetValueTaskSourceCore结构,最新的框架版本都有,没有的话可以在nuget上搜搜看
注意它是一个结构并且没有实现任何接口,所以要使用的话必须包装一下,结构字段千万不能设置为readonly
public sealed class ValueTaskSource
{
private ManualResetValueTaskSourceCore
public bool RunContinuationsAsynchronously
{
get => m_source.RunContinuationsAsynchronously;
set => m_source.RunContinuationsAsynchronously = value;
}
public short Version => m_source.Version;
public void Reset() => m_source.Reset();
public void SetResult(T result) => m_source.SetResult(result);
public void SetException(Exception error) => m_source.SetException(error);
public T GetResult(short token) => m_source.GetResult(token);
void IValueTaskSource.GetResult(short token) => m_source.GetResult(token);
public ValueTaskSourceStatus GetStatus(short token)
=> m_source.GetStatus(token);
public void OnCompleted(Action
浙公网安备 33010602011771号