ValueTask.Preserve() 的意义何在?
What is the point of ValueTask.Preserve()?
ValueTask
和 ValueTask<TResult>
有一个 Preserve()
方法,总结为“获取一个 ValueTask,可以在将来的任何时候使用。”
这是什么意思,我们应该在什么时候使用它?这是否意味着 'normal' ValueTask
不能“在未来的任何时候”使用?如果是,为什么?
文档不是很清楚。来自 the source :
/// <summary>null if representing a successful synchronous completion,
/// otherwise a <see cref="Task"/> or a <see cref="IValueTaskSource"/>.</summary>
internal readonly object? _obj;
ValueTask
在内部声明了一个可为 null 的容器对象来保存 ValueTask
封装的 Task
或 IValueTaskSource
应该是 ValueTask
所以。如果 ValueTask
表示完成,则为 null
。
public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask());
Preserve()
returns 一个新的 ValueTask
表示其基础任务,在 ValueTask
表示 Task
或 IValueTaskSource
的情况下(即:_obj != null
)或者如果 ValueTask
表示成功的同步完成,则只是 returns 本身。这是必要的,因为 _obj
是内部的,无法在外部进行测试。
ValueTask
is a performance optimization over a Task
s, but this performance comes with a cost: You cannot use a ValueTask
as freely as a Task
. The documentation 提到了这些限制:
The following operations should never be performed on a ValueTask
instance:
- Awaiting the instance multiple times.
- Calling
AsTask
multiple times.
- Using more than one of these techniques to consume the instance.
这些限制仅适用于由 IValueTaskSource
支持的 ValueTask
。 ValueTask
也可以由 Task
或 TResult
值支持(对于 ValueTask<TResult>
)。如果您 100% 确定 ValueTask
不受 IValueTaskSource
支持,您可以像 Task
一样自由使用它。例如你可以 await
多次,或者你可以用 .GetAwaiter().GetResult()
.
同步等待它完成
通常您不知道 ValueTask
是如何在内部实现的,即使您知道(通过研究源代码)您也可能不想依赖实现细节。使用 ValueTask.Preserve
方法,您可以创建一个新的 ValueTask
来表示原始 ValueTask
,并且可以不受限制地使用,因为它不是由 IValueTaskSource
烘焙的。此方法仅在 IValueTaskSource
支持时才影响原始 ValueTask
,否则它是 no-op(它只是 returns 未更改的原始任务)。
ValueTask preserved = originalValueTask.Preserve();
调用Preserve
后,原来的ValueTask
已经消耗掉了。它不应再等待,或再次 Preserve
d,或使用 AsTask
方法转换为 Task
。执行任何这些操作都可能导致 InvalidOperationException
。但是现在你有了它的 preserved
表示,它可以像 Task
.
一样自由使用
我的回答最初包括一个使用Preserve
方法的实际例子,结果证明是不正确的。这个例子可以在这个答案的 4th revision 中找到。
ValueTask
和 ValueTask<TResult>
有一个 Preserve()
方法,总结为“获取一个 ValueTask,可以在将来的任何时候使用。”
这是什么意思,我们应该在什么时候使用它?这是否意味着 'normal' ValueTask
不能“在未来的任何时候”使用?如果是,为什么?
文档不是很清楚。来自 the source :
/// <summary>null if representing a successful synchronous completion,
/// otherwise a <see cref="Task"/> or a <see cref="IValueTaskSource"/>.</summary>
internal readonly object? _obj;
ValueTask
在内部声明了一个可为 null 的容器对象来保存 ValueTask
封装的 Task
或 IValueTaskSource
应该是 ValueTask
所以。如果 ValueTask
表示完成,则为 null
。
public ValueTask Preserve() => _obj == null ? this : new ValueTask(AsTask());
Preserve()
returns 一个新的 ValueTask
表示其基础任务,在 ValueTask
表示 Task
或 IValueTaskSource
的情况下(即:_obj != null
)或者如果 ValueTask
表示成功的同步完成,则只是 returns 本身。这是必要的,因为 _obj
是内部的,无法在外部进行测试。
ValueTask
is a performance optimization over a Task
s, but this performance comes with a cost: You cannot use a ValueTask
as freely as a Task
. The documentation 提到了这些限制:
The following operations should never be performed on a
ValueTask
instance:
- Awaiting the instance multiple times.
- Calling
AsTask
multiple times.- Using more than one of these techniques to consume the instance.
这些限制仅适用于由 IValueTaskSource
支持的 ValueTask
。 ValueTask
也可以由 Task
或 TResult
值支持(对于 ValueTask<TResult>
)。如果您 100% 确定 ValueTask
不受 IValueTaskSource
支持,您可以像 Task
一样自由使用它。例如你可以 await
多次,或者你可以用 .GetAwaiter().GetResult()
.
通常您不知道 ValueTask
是如何在内部实现的,即使您知道(通过研究源代码)您也可能不想依赖实现细节。使用 ValueTask.Preserve
方法,您可以创建一个新的 ValueTask
来表示原始 ValueTask
,并且可以不受限制地使用,因为它不是由 IValueTaskSource
烘焙的。此方法仅在 IValueTaskSource
支持时才影响原始 ValueTask
,否则它是 no-op(它只是 returns 未更改的原始任务)。
ValueTask preserved = originalValueTask.Preserve();
调用Preserve
后,原来的ValueTask
已经消耗掉了。它不应再等待,或再次 Preserve
d,或使用 AsTask
方法转换为 Task
。执行任何这些操作都可能导致 InvalidOperationException
。但是现在你有了它的 preserved
表示,它可以像 Task
.
我的回答最初包括一个使用Preserve
方法的实际例子,结果证明是不正确的。这个例子可以在这个答案的 4th revision 中找到。