0

最近,我正在阅读Jon Skeet 博客中的Eduasync 连载。当我阅读第 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;
    }
} 

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

4

1 回答 1

1

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

  1. 异步操作开始
  2. IsCompleted被检查,这将返回false,因为操作还没有完成
  3. 操作完成
  4. OnCompleted()被调用,而后者又调用ContinueWith(),但由于Task已经完成,延续永远不会执行

如果我猜对了,那么你的错误就在第 4 步。那是因为 的作者Task知道这种竞争条件,所以如果你调用ContinueWith()一个已经完成的,那么将立即安排Task继续。因此,即使在这种情况下,状态机也能正常工作。

不幸的是,关于这一点的文档ContinueWith()不是很清楚(它解释了何时不会安排延续,但没有解释何时会)。

于 2012-07-24T06:46:35.107 回答