英文:
How can I synchronously wait for ValueTask without performance loss?
问题
首先,我们的平台是C# 9.0和.NET Core 7.0。
假设我必须在同步方法中等待ValueTask
完成。在处理这种情况下的任务时,我通常使用以下方案:
var task = Get_Task_PlaceHolderMethod();
task.Wait(timeoutedToken);
var value = task.Result // 从这里开始使用
否则,我只需使用await
。
那么,如何在等待ValueTask
时使用类似的方法?它们没有任何.Wait
方法,我只能将其转换为任务并使用.AsTask()
,但这样我会失去ValueTask
创建任务实例的好处。
我尝试编写了一些逻辑,但总是得到比以前性能更差的代码。如下所示,我不得不创建一个任务的实例,因此上述影响也适用。我无法从ValueTask
中获益。
private static void SyncallyAwait(Func<ValueTask> getTask, TimeSpan timeout)
{
var operationWaiter = new CancellationTokenSource();
var v_task = getTask();
var timeouter = Task.Delay(timeout, operationWaiter.Token);
while ( // 等待完成
v_task.IsCompleted is false &&
timeouter.IsCompleted is false
){ }
if (timeouter.IsCompleted)
throw ERROR_TaskTimeouted();
operationWaiter.Cancel();
if (v_task.IsCompleted is false)
throw ERROR_TaskNotAccessible();
// 抛出任何结果异常
v_task.GetAwaiter().GetResult();
}
还有另一种可能性,我可以尝试使用v_task
的开始时间,并接收一个TimeSpan
来在循环中计算当前时间,同时检查Now - start_time
是否大于限制的timeSpan
。但这似乎不太合适。
英文:
First of all, our platform for this question is C# 9.0 and .NET core 7.0.
Lets suppose that I have to wait for a ValueTask
to complete in a synchronous method. When dealing with tasks in this context, I generally use the following scheme:
var task = Get_Task_PlaceHolderMethod();
task.Wait(timeoutedToken);
var value = task.Result // use from here
Otherwise I just await
, normally.
how can I use something like this to wait for ValueTasks? They don't have any .Wait
method and all I can do is convert it to task with .AsTask()
, but then I will lose the benefits of the ValueTask
creating an instance of a task to wrap it.
I tried to code some logic but always end up with less performant code than before.
As following I had to create an instance of a task, so the above implications applies too.
I have no benefit from the ValueTask
.
private static void SyncallyAwait(Func<ValueTask> getTask, TimeSpan timeout)
{
var operationWaiter = new CancellationTokenSource();
var v_task = getTask();
var timeouter = Task.Delay(timeout, operationWaiter.Token);
while ( // wait until done
v_task.IsCompleted is false &&
timeouter.IsCompleted is false
){ }
if (timeouter.IsCompleted)
throw ERROR_TaskTimeouted();
operationWaiter.Cancel();
if (v_task.IsCompleted is false)
throw ERROR_TaskNotAccessible();
//throw any resulting exception
v_task.GetAwaiter().GetResult();
}
private static readonly Func<ApplicationException> ERROR_TaskNotAccessible = () => new($"""
Its not possible to ensure that the valuetask is done running, which means that the
result is not accurate or safe to access without putting the thread on deadlock.
to prevent the unexpected lack of response this exception has been thrown
""");
private static readonly Func<TimeoutException> ERROR_TaskTimeouted = () => new($"""
MovingOperation exceeded the sync waiter timeout. The database didn't returned in
reasonable time expected.
""");
Another possibility I could try is to use the start time of the v_task
and receive a TimeSpan
to count the current time in the loop while, checking if the Now - start_time
is
greater than the limit timeSpan
. But that doesn't seem right.
答案1
得分: 2
我并没有经常使用值任务。但我理解主要的好处是避免在结果已经可用且异步方法可以同步完成时分配Task
对象。所以我认为正确的方法应该是:
var valueTask = getTask();
if (valueTask.IsCompleted)
{
return valueTask.Result;
}
return valueTask.AsTask().Result;
如果getTask()
确实是同步运行的,那么返回的值任务将已经完成,您只需检查并返回结果。如果它没有同步完成,您必须将其转换为任务并等待任务完成。我不认为这会引起任何性能影响。
您的示例似乎在自旋等待值任务,这几乎肯定不是一个好的方法。
英文:
I have not used value tasks that much. But my understanding is that the primary benefit is avoiding allocation of a Task
object when the result is already available and the async method can complete synchronously. So I think the correct approach should be:
var valueTask = getTask();
if(valueTask.IsCompleted){
return valueTask.Result;
}
return valueTask.AsTask().Result;
If getTask()
was really run synchronously, then the returned value task will already be completed, and you can just check this and return the result. If it did not complete synchronously you have to convert it to a task and wait on the task. I do not see any reason why this should cause any performance impact.
Your example seem to be spinwaiting on the value task, and that is almost certainly not a good approach.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论