最近,我正在Jon Skeet’s blog阅读Eduasync序列号。当我在part 7读到时,有一个问题阻止了我,我认为C#生成的状态机在某些极少数情况下可能无法正常工作,让我们深入代码(这段代码来自Jon Skeet的Eduasync第7部分,我只是添加了一些评价):
public void MoveNext()
{
int result;
try
{ // doFinallyBodies is never used
bool doFinallyBodies = true;
if (state != 1)
{
if (state != -1)
{
task = Task<int>.Factory.StartNew(() => 5);
awaiter = task.GetAwaiter();
// In a rare case, in this moment the task still has not completed,
// so return false IsCompleted
if (awaiter.IsCompleted)
{
goto Label_GetResult;
}
state = 1;
// The task just completed before OnCompleted,
// but in this moment we haven't call the OnCompleted yet,
// so the task's ContinueWith is nothing the task will complete
// without ContinueWith and we will never get back to this StateMachine again.
doFinallyBodies = false;
awaiter.OnCompleted(moveNextDelegate);
}
return;
}
state = 0;
Label_GetResult:
int awaitResult = awaiter.GetResult();
awaiter = new TaskAwaiter<int>();
result = awaitResult;
}
catch (Exception e)
{
state = -1;
builder.SetException(e);
return;
}
state = -1;
builder.SetResult(result);
}
public struct TaskAwaiter<T>
{
private readonly Task<T> task;
internal TaskAwaiter(Task<T> task)
{
this.task = task;
}
public bool IsCompleted { get { return task.IsCompleted; } }
public void OnCompleted(Action action)
{
SynchronizationContext context = SynchronizationContext.Current;
TaskScheduler scheduler = context == null ? TaskScheduler.Current
: TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(ignored => action(), scheduler);
}
public T GetResult()
{
return task.Result;
}
}
那么你认为这可能是一个问题吗?
答案 0 :(得分:1)
为了确保我理解正确,我将更详细地描述我认为您期待的事件序列:
IsCompleted
,这将返回false
,因为操作尚未完成OnCompleted()
被调用,后者又调用ContinueWith()
,但由于Task
已经完成,因此永远不会执行延续如果我认为这是正确的,那么你的错误就在第4步。那是因为Task
的作者知道这种竞争条件,所以如果你在已经完成的ContinueWith()
上调用Task
1}},继续将立即安排。因此,即使在这种情况下,状态机也能正常工作。
不幸的是,the documentation for ContinueWith()
对此并不十分清楚(它解释了延迟何时不会安排,但不会安排时间。)