.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的目的,就是为了在一些特定的场景下,替换Task的使用来减少资源的浪费,
比如同步完成的异步方法的返回值,因为ValueTask是值类型所以能够减少堆中的分配
然而 ValueTask的最主要的作用就是包装自定义的等待对象,功能跟Task类似,只不过功能比Task少,并且根据情况内部可以重复使用该对象,也正是因为如此,所以文档中说 ValueTask只能等待一次,或者说直接转换为Task再瞎搞,因为一个等待完成,该内部对象可能已经被重置状态复用了
然而假如要自己使用ValueTask的最重要的功能,也就是自定义的等待对象,需要自己实现
庆幸的是类库公开了一个简单的内部类型的简单实现,也就是ManualResetValueTaskSourceCore结构,最新的框架版本都有,没有的话可以在nuget上搜搜看
注意它是一个结构并且没有实现任何接口,所以要使用的话必须包装一下,结构字段千万不能设置为readonly

public sealed class ValueTaskSource : IValueTaskSource, IValueTaskSource
{

private ManualResetValueTaskSourceCore m_source;

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 continuation, object state,
short token, ValueTaskSourceOnCompletedFlags flags)
=> m_source.OnCompleted(continuation, state, token, flags);
}

大体上可以这样使用

static ValueTask GetTask()
{
//定情信物
//个人理解主要用来确定是否在上一个使用者完事之前,就已经被别人用了
short v = 521;

var source = new ValueTaskSource();

//模拟延迟完成
Task.Delay(5000)
.ContinueWith((t) => source.SetResult(new Uri("https://www.pornhub.com")));

return new ValueTask(source, v);

}

posted @ 2021-03-31 08:13  FfD4edyo  阅读(224)  评论(0)    收藏  举报