这对于异步生成的StateMachine中的MoveNext是否有意义

时间:2012-07-24 01:55:55

标签: c# .net asynchronous async-await c#-5.0

最近,我正在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;
    }
} 

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

1 个答案:

答案 0 :(得分:1)

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

  1. 启动异步操作
  2. 选中
  3. IsCompleted,这将返回false,因为操作尚未完成
  4. 操作完成
  5. OnCompleted()被调用,后者又调用ContinueWith(),但由于Task已经完成,因此永远不会执行延续
  6. 如果我认为这是正确的,那么你的错误就在第4步。那是因为Task的作者知道这种竞争条件,所以如果你在已经完成的ContinueWith()上调用Task 1}},继续将立即安排。因此,即使在这种情况下,状态机也能正常工作。

    不幸的是,the documentation for ContinueWith()对此并不十分清楚(它解释了延迟何时不会安排,但不会安排时间。)