亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在.NET中產生并等待實現控制流?

如何在.NET中產生并等待實現控制流?

慕斯709654 2019-12-10 10:48:42
據我了解yield,如果從迭代器塊內部使用該關鍵字,它將控制流返回到調用代碼,并且當再次調用該迭代器時,它將從中斷的地方開始。同樣,await不僅要等待被調用方,而且還會將控制權返回給調用方,僅在調用方awaits方法時從中斷處接管。換句話說,沒有線程,異步和等待的“并發性”是由聰明的控制流引起的錯覺,其細節被語法隱藏了?,F在,我是一名前匯編程序員,并且對指令指針,堆棧等非常熟悉,并且了解了正常的控制流程(子例程,遞歸,循環,分支)的工作方式。但是這些新結構-我不明白。當await到達,如何運行時知道什么是一段代碼下一步應該執行?它如何知道何時可以從上次中斷的地方恢復,以及如何記住在哪里?當前調用堆棧發生了什么,是否以某種方式保存了它?如果調用方法在此之前進行其他方法調用await怎么辦-為什么堆棧不被覆蓋?在異常和堆棧展開的情況下,運行時到底將如何處理所有這些問題?何時yield到達,運行時如何跟蹤應該拾取的點?迭代器狀態如何保存?
查看完整描述

3 回答

?
梵蒂岡之花

TA貢獻1900條經驗 獲得超5個贊

yield 是兩者中比較容易的一個,所以讓我們檢查一下。


說我們有:


public IEnumerable<int> CountToTen()

{

  for (int i = 1; i <= 10; ++i)

  {

    yield return i;

  }

}

這被編譯一個位,如果我們想這樣寫的:


// Deliberately use name that isn't valid C# to not clash with anything

private class <CountToTen> : IEnumerator<int>, IEnumerable<int>

{

    private int _i;

    private int _current;

    private int _state;

    private int _initialThreadId = CurrentManagedThreadId;


    public IEnumerator<CountToTen> GetEnumerator()

    {

        // Use self if never ran and same thread (so safe)

        // otherwise create a new object.

        if (_state != 0 || _initialThreadId != CurrentManagedThreadId)

        {

            return new <CountToTen>();

        }


        _state = 1;

        return this;

    }


    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();


    public int Current => _current;


    object IEnumerator.Current => Current;


    public bool MoveNext()

    {

        switch(_state)

        {

            case 1:

                _i = 1;

                _current = i;

                _state = 2;

                return true;

            case 2:

                ++_i;

                if (_i <= 10)

                {

                    _current = _i;

                    return true;

                }

                break;

        }

        _state = -1;

        return false;

    }


    public void Dispose()

    {

      // if the yield-using method had a `using` it would

      // be translated into something happening here.

    }


    public void Reset()

    {

        throw new NotSupportedException();

    }

}

所以,還不如一個手寫的執行效率IEnumerable<int>和IEnumerator<int>(例如,我們可能不會有一個單獨的浪費_state,_i而且_current在這種情況下),但不壞(的伎倆再利用自身安全情況下這樣做,而不是創建一個新的對象是好的),并且可以擴展以處理非常復雜的yield使用方法。


當然,因為


foreach(var a in b)

{

  DoSomething(a);

}

是相同的:


using(var en = b.GetEnumerator())

{

  while(en.MoveNext())

  {

     var a = en.Current;

     DoSomething(a);

  }

}

然后,生成的MoveNext()被重復調用。


這種async情況幾乎是相同的原理,但是有一些額外的復雜性。重用另一個答案代碼中的示例,例如:


private async Task LoopAsync()

{

    int count = 0;

    while(count < 5)

    {

       await SomeNetworkCallAsync();

       count++;

    }

}

產生如下代碼:


private struct LoopAsyncStateMachine : IAsyncStateMachine

{

  public int _state;

  public AsyncTaskMethodBuilder _builder;

  public TestAsync _this;

  public int _count;

  private TaskAwaiter _awaiter;

  void IAsyncStateMachine.MoveNext()

  {

    try

    {

      if (_state != 0)

      {

        _count = 0;

        goto afterSetup;

      }

      TaskAwaiter awaiter = _awaiter;

      _awaiter = default(TaskAwaiter);

      _state = -1;

    loopBack:

      awaiter.GetResult();

      awaiter = default(TaskAwaiter);

      _count++;

    afterSetup:

      if (_count < 5)

      {

        awaiter = _this.SomeNetworkCallAsync().GetAwaiter();

        if (!awaiter.IsCompleted)

        {

          _state = 0;

          _awaiter = awaiter;

          _builder.AwaitUnsafeOnCompleted<TaskAwaiter, TestAsync.LoopAsyncStateMachine>(ref awaiter, ref this);

          return;

        }

        goto loopBack;

      }

      _state = -2;

      _builder.SetResult();

    }

    catch (Exception exception)

    {

      _state = -2;

      _builder.SetException(exception);

      return;

    }

  }

  [DebuggerHidden]

  void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0)

  {

    _builder.SetStateMachine(param0);

  }

}


public Task LoopAsync()

{

  LoopAsyncStateMachine stateMachine = new LoopAsyncStateMachine();

  stateMachine._this = this;

  AsyncTaskMethodBuilder builder = AsyncTaskMethodBuilder.Create();

  stateMachine._builder = builder;

  stateMachine._state = -1;

  builder.Start(ref stateMachine);

  return builder.Task;

}

它比較復雜,但是基本原理非常相似。最主要的復雜之處在于現在GetAwaiter()正在使用它。如果awaiter.IsCompleted檢查了任何時間,則true由于任務awaited已經完成(例如,它可以同步返回),該方法將返回,然后該方法將繼續遍歷狀態,否則將其自身設置為對等待者的回調。


究竟發生什么取決于等待者,包括觸發回調的原因(例如異步I / O完成,在線程上運行的任務完成)以及對編組到特定線程或在線程池線程上運行有什么要求,可能需要也可能不需要原始調用的上下文,依此類推。無論該等待者中的內容是什么,都會調用MoveNext,它將繼續進行下一個工作(直到下一個工作await),或者完成并返回,在這種情況下Task,實現的對象將完成。


查看完整回答
反對 回復 2019-12-10
  • 3 回答
  • 0 關注
  • 537 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號