C# 这对异步生成的状态机中的MoveNext有意义吗

C# 这对异步生成的状态机中的MoveNext有意义吗,c#,.net,asynchronous,async-await,c#-5.0,C#,.net,Asynchronous,Async Await,C# 5.0,最近,我在读Eduasync serial。当我阅读时,有一个问题阻碍了我,我认为C生成的状态机在某些罕见的情况下可能无法正常工作,让我们深入了解代码(此代码来自Jon Skeet的Eduancy第7部分,我只是添加了一些建议): public void MoveNext() { int结果; 尝试 {//doFinallyBodies从不使用 bool doFinallyBodies=true; 如果(状态!=1) { 如果(状态!=-1) { task=task.Factory.Sta

最近,我在读Eduasync serial。当我阅读时,有一个问题阻碍了我,我认为C生成的状态机在某些罕见的情况下可能无法正常工作,让我们深入了解代码(此代码来自Jon Skeet的Eduancy第7部分,我只是添加了一些建议):

public void MoveNext()
{ 
int结果;
尝试
{//doFinallyBodies从不使用
bool doFinallyBodies=true;
如果(状态!=1)
{ 
如果(状态!=-1)
{ 
task=task.Factory.StartNew(()=>5);
waiter=task.getwaiter();
//在极少数情况下,此时任务仍未完成,
//所以返回false就完成了
如果(等待者完成)
{ 
转到标签获取结果;
} 
状态=1;
//在未完成之前刚刚完成的任务,
//但在这一刻,我们还没有宣布未完成,
//因此,任务的连续性意味着任务将无法完成
//如果没有ContinueWith,我们就再也不会回到这个状态机了。
doFinallyBodies=false;
等待者。未完成(移动下一步删除);
} 
返回;
} 
状态=0;
标签\u获取结果:
int waitresult=waiter.GetResult();
等待者=新任务等待者();
结果=等待结果;
} 
捕获(例外e)
{ 
状态=-1;
builder.SetException(e);
返回;
} 
状态=-1;
builder.SetResult(result);
} 
公共结构任务等待器
{
私有只读任务;
内部任务等待者(任务)
{
this.task=任务;
}
public bool IsCompleted{get{return task.IsCompleted;}
未完成公共作废(行动)
{
SynchronizationContext=SynchronizationContext.Current;
TaskScheduler=context==null?TaskScheduler.Current
:TaskScheduler.FromCurrentSynchronizationContext();
task.ContinueWith(忽略=>action(),调度程序);
}
公共T GetResult()
{
返回任务。结果;
}
} 

那么你认为这可能是个问题吗?

为了确保我正确理解你,我将更详细地描述我认为你期待的事件顺序:

  • 异步操作已启动
  • 选中了
    IsCompleted
    ,这将返回
    false
    ,因为操作尚未完成
  • 操作完成
  • 调用了
    OnCompleted()
    ,这又调用了
    ContinueWith()
    ,但是由于
    任务已经完成,所以continuation永远不会执行
  • 如果我答对了,那么你的错误在第四步。这是因为
    任务
    的作者知道这种竞争条件,因此,如果您对已完成的
    任务
    调用
    ContinueWith()
    ,将立即安排继续。因此,即使在这种情况下,状态机也能正常工作


    不幸的是,我对此不是很清楚(它解释了什么时候不安排继续,但不是什么时候安排)。

    是的,这正是我想问的。谢谢你的回答,这对我来说很有意义。顺便问一下,除了MSDN文档外,您能否提供有关ContinueWith()的更多信息“将立即安排”。这仅仅意味着
    任务将添加到当前
    任务计划程序中。然后由调度程序决定何时执行。这就是你要问的吗?
    
    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;
        }
    }