3 回答
TA贡献1900条经验 获得超5个赞
yield 是两者中比较容易的一个,所以让我们检查一下。
说我们有:
public IEnumerable<int> CountToTen()
{
for (int i = 1; i <= 10; ++i)
{
yield return i;
}
}
这被编译一个位,如果我们想这样写的:
// Deliberately use name that isn't valid C# to not clash with anything
private class <CountToTen> : IEnumerator<int>, IEnumerable<int>
{
private int _i;
private int _current;
private int _state;
private int _initialThreadId = CurrentManagedThreadId;
public IEnumerator<CountToTen> GetEnumerator()
{
// Use self if never ran and same thread (so safe)
// otherwise create a new object.
if (_state != 0 || _initialThreadId != CurrentManagedThreadId)
{
return new <CountToTen>();
}
_state = 1;
return this;
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int Current => _current;
object IEnumerator.Current => Current;
public bool MoveNext()
{
switch(_state)
{
case 1:
_i = 1;
_current = i;
_state = 2;
return true;
case 2:
++_i;
if (_i <= 10)
{
_current = _i;
return true;
}
break;
}
_state = -1;
return false;
}
public void Dispose()
{
// if the yield-using method had a `using` it would
// be translated into something happening here.
}
public void Reset()
{
throw new NotSupportedException();
}
}
所以,还不如一个手写的执行效率IEnumerable<int>和IEnumerator<int>(例如,我们可能不会有一个单独的浪费_state,_i而且_current在这种情况下),但不坏(的伎俩再利用自身安全情况下这样做,而不是创建一个新的对象是好的),并且可以扩展以处理非常复杂的yield使用方法。
当然,因为
foreach(var a in b)
{
DoSomething(a);
}
是相同的:
using(var en = b.GetEnumerator())
{
while(en.MoveNext())
{
var a = en.Current;
DoSomething(a);
}
}
然后,生成的MoveNext()被重复调用。
这种async情况几乎是相同的原理,但是有一些额外的复杂性。重用另一个答案代码中的示例,例如:
private async Task LoopAsync()
{
int count = 0;
while(count < 5)
{
await SomeNetworkCallAsync();
count++;
}
}
产生如下代码:
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;
}
它比较复杂,但是基本原理非常相似。最主要的复杂之处在于现在GetAwaiter()正在使用它。如果awaiter.IsCompleted检查了任何时间,则true由于任务awaited已经完成(例如,它可以同步返回),该方法将返回,然后该方法将继续遍历状态,否则将其自身设置为对等待者的回调。
究竟发生什么取决于等待者,包括触发回调的原因(例如异步I / O完成,在线程上运行的任务完成)以及对编组到特定线程或在线程池线程上运行有什么要求,可能需要也可能不需要原始调用的上下文,依此类推。无论该等待者中的内容是什么,都会调用MoveNext,它将继续进行下一个工作(直到下一个工作await),或者完成并返回,在这种情况下Task,实现的对象将完成。
- 3 回答
- 0 关注
- 481 浏览
添加回答
举报