在循环内,是否使用任务的continuewith将每个异步调用链接到返回的任务? [英] Inside a loop,does each async call get chained to the returned task using task's continuewith?
问题描述
最佳实践是在循环内的集合中收集所有async
调用,然后执行Task.WhenAll()
.但是,想了解在循环内遇到await
时会发生什么,返回的Task
将包含什么?接下来的async
调用怎么办?它会创建新任务并将它们依次添加到已经返回的Task
吗?
The best practice is to collect all the async
calls in a collection inside the loop and do Task.WhenAll()
. Yet, want to understand what happens when an await
is encountered inside the loop, what would the returned Task
contain? what about further async
calls? Will it create new tasks and add them to the already returned Task
sequentially?
按照下面的代码
private void CallLoopAsync()
{
var loopReturnedTask = LoopAsync();
}
private async Task LoopAsync()
{
int count = 0;
while(count < 5)
{
await SomeNetworkCallAsync();
count++;
}
}
我假设的步骤是
-
LoopAsync
被呼叫 -
count
设置为零,循环时输入代码,检查条件
调用 -
SomeNetworkCallAsync
,等待返回的任务 - 已创建新任务/可等待任务
- 新任务返回到
CallLoopAsync
()
LoopAsync
gets calledcount
is set to zero, code enters while loop, condition is checkedSomeNetworkCallAsync
is called,and the returned task is awaited- New task/awaitable is created
- New task is returned to
CallLoopAsync
()
现在,只要有足够的时间使进程运行,如何/以何种方式执行下一个代码行,如count++
和进一步的SomeNetworkCallAsync
?
Now, provided there is enough time for the process to live, How / In what way, will the next code lines like count++
and further SomeNetworkCallAsync
be executed?
更新-基于乔恩·汉纳和
因此,只有一项任务,该任务的实施将涉及
5个调用NetworkCallAsync,但使用状态机的方式
这些任务无需显式链接即可运行.这个,对于
例如,允许它根据是否决定中断循环
任务的结果等等.
So there is one Task and the implementation of that Task will involve
5 calls to NetworkCallAsync, but the use of a state-machine means
those tasks need not be explicitly chained for this to work. This, for
example, allows it to decide whether to break the looping or not based
on the result of a task, and so on. 尽管它们未链接在一起,但每个调用都将等待上一个调用完成,因为我们已经使用了 Though they are not chained, each call will wait for the previous call to complete as we have used 代替写作 它应该写成 由于 parallelism 这是正确的吗? - 是的.除了"一路异步"之外,"一路累积任务"也是一种很好的做法.类似的讨论是此处 Is this correct? - Yes. Along with "async all the way", "Accumulate tasks all the way" is good practice. Similar discussion is here 要生成类似于 To produce code similar to what (以上内容基于您使用 (The above is based on what happens when you use 结果是来自 The result is a single Task which comes from 当最终达到 When it finally reaches a 因此有一个 So there is one 这篇关于在循环内,是否使用任务的continuewith将每个异步调用链接到返回的任务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
await
(处于状态m/c,awaiter.GetResult();
).它的行为就好像已经进行了五个连续调用,并且一个接一个地执行(仅在先前调用之后完成后 ).如果这是真的,那么我们在编写异步调用的方式上必须更加谨慎.例如:await
(in state m/c, awaiter.GetResult();
). It behaves as if five consecutive calls have been made and they are executed one after the another (only after the previous call gets completed). If this is true, we have to be bit more careful in how we are composing the async calls.For ex:private async Task SomeWorkAsync()
{
await SomeIndependentNetworkCall();// 2 sec to complete
var result1 = await GetDataFromNetworkCallAsync(); // 2 sec to complete
await PostDataToNetworkAsync(result1); // 2 sec to complete
}
private Task[] RefactoredSomeWorkAsync()
{
var task1 = SomeIndependentNetworkCall();// 2 sec to complete
var task2 = GetDataFromNetworkCallAsync()
.ContinueWith(result1 => PostDataToNetworkAsync(result1)).Unwrap();// 4 sec to complete
return new[] { task1, task2 };
}
private async Task CallRefactoredSomeWorkAsync()
{
await Task.WhenAll(RefactoredSomeWorkAsync());//Faster, 4 sec
await SomeWorkAsync(); // Slower, 6 sec
}
推荐答案
async
和await
的代码,如果这些关键字不存在,则需要类似以下的代码:async
and await
do, if those keywords didn't exist, would require code a bit like:private struct LoopAsyncStateMachine : IAsyncStateMachine
{
public int _state;
public AsyncTaskMethodBuilder _builder;
public TestAsync _this;
public int _count;
private TaskAwaiter _awaiter;
void IAsyncStateMachine.MoveNext()
{
try
{
if (_state != 0)
{
_count = 0;
goto afterSetup;
}
TaskAwaiter awaiter = _awaiter;
_awaiter = default(TaskAwaiter);
_state = -1;
loopBack:
awaiter.GetResult();
awaiter = default(TaskAwaiter);
_count++;
afterSetup:
if (_count < 5)
{
awaiter = _this.SomeNetworkCallAsync().GetAwaiter();
if (!awaiter.IsCompleted)
{
_state = 0;
_awaiter = awaiter;
_builder.AwaitUnsafeOnCompleted<TaskAwaiter, TestAsync.LoopAsyncStateMachine>(ref awaiter, ref this);
return;
}
goto loopBack;
}
_state = -2;
_builder.SetResult();
}
catch (Exception exception)
{
_state = -2;
_builder.SetException(exception);
return;
}
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)
{
_builder.SetStateMachine(param0);
}
}
public Task LoopAsync()
{
LoopAsyncStateMachine stateMachine = new LoopAsyncStateMachine();
stateMachine._this = this;
AsyncTaskMethodBuilder builder = AsyncTaskMethodBuilder.Create();
stateMachine._builder = builder;
stateMachine._state = -1;
builder.Start(ref stateMachine);
return builder.Task;
}
async
和await
时发生的情况,不同的是,结果使用的名称可能不是有效的C#类或字段名称,以及一些其他属性.如果MoveNext()
让您想起不是完全无关的IEnumerator
,await
和async
产生IAsyncStateMachine
来实现Task
的机制在许多方面与yield
产生IEnumerator<T>
的方式相似) async
and await
except that the result of that uses names that cannot be valid C# class or field names, along with some extra attributes. If its MoveNext()
reminds you of an IEnumerator
that's not entirely irrelevant, the mechanism by which await
and async
produce an IAsyncStateMachine
to implement a Task
is similar in many ways to how yield
produces an IEnumerator<T>
).AsyncTaskMethodBuilder
的单个Task,并使用了LoopAsyncStateMachine
(它与async
生成的隐藏struct
接近).在启动任务时首先调用其MoveNext()
方法.然后它将在SomeNetworkCallAsync
上使用一个等待者.如果已经完成,则进入下一个阶段(递增count
,依此类推),否则将等待者存储在字段中.在随后的使用中,由于SomeNetworkCallAsync()
任务已返回,因此将调用该方法,它将获得结果(在这种情况下为空,但如果返回值则可以为值).然后,它尝试进一步循环,并在等待尚未完成的任务时再次返回.AsyncTaskMethodBuilder
and makes use of LoopAsyncStateMachine
(which is close to the hidden struct
that the async
produces). Its MoveNext()
method is first called upon the task being started. It will then use an awaiter on SomeNetworkCallAsync
. If it is already completed it moves on to the next stage (increment count
and so on), otherwise it stores the awaiter in a field. On subsequent uses it will be called because the SomeNetworkCallAsync()
task has returned, and it will get the result (which is void in this case, but could be a value if values were returned). It then attempts further loops and again returns when it is waiting on a task that is not yet completed.count
的5时,它将在构建器上调用SetResult()
,从而设置LoopAsync
返回的Task
的结果.count
of 5 it calls SetResult()
on the builder, which sets the result of the Task
that LoopAsync
had returned.Task
,该Task
的实现将涉及对NetworkCallAsync
的5次调用,但是使用状态机意味着不需要为这些任务明确地链接这些任务即可工作.例如,这使它可以根据任务的结果来决定是否中断循环,等等.Task
and the implementation of that Task
will involve 5 calls to NetworkCallAsync
, but the use of a state-machine means those tasks need not be explicitly chained for this to work. This, for example, allows it to decide whether to break the looping or not based on the result of a task, and so on.