0

最近、私はJonSkeetのブログでEduasyncシリアルを読んでいます。パート7を読んだとき、1つの質問が私をブロックしました。まれに、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 に答える