하이브리드 유형입니까? (예를 들어, 내 .NET 프로그램은 비동기 호출에 도달 할 때까지 스택을 사용하고 완료 될 때까지 다른 구조로 전환합니까?이 시점에서 스택은 다음 항목을 확인할 수있는 상태로 풀립니다. )
기본적으로 그렇습니다.
우리가 가지고 있다고 가정
async void MyButton_OnClick() { await Foo(); Bar(); }
async Task Foo() { await Task.Delay(123); Blah(); }
다음 은 연속이 어떻게 구체화되는지에 대한 매우 간단한 설명입니다. 실제 코드는 훨씬 더 복잡하지만 아이디어를 얻습니다.
버튼을 클릭하십시오. 메시지가 대기 중입니다. 메시지 루프는 메시지를 처리하고 클릭 핸들러를 호출하여 메시지 큐의 리턴 주소를 스택에 둡니다. 즉, 핸들러가 완료된 후에 발생하는 것은 메시지 루프가 계속 실행되어야한다는 것입니다. 따라서 처리기의 연속은 루프입니다.
클릭 핸들러는 Foo ()를 호출하여 스택의 리턴 주소를 스택에 넣습니다. 즉, Foo의 연속은 나머지 클릭 처리기입니다.
Foo는 Task.Delay를 호출하여 자신의 반환 주소를 스택에 넣습니다.
Task.Delay는 즉시 작업을 반환하기 위해 필요한 모든 마법을 수행합니다. 스택이 튀어 나와 Foo로 돌아 왔습니다.
Foo는 반환 된 작업이 완료되었는지 확인합니다. 그렇지 않습니다. 계속 의 AWAIT은 푸 작업의 연속으로 최대 위임 할 것을 어쩌구를 (호출하는 대리자를) 생성 및 징후, 그래서) 어쩌구를 (호출하는 것입니다. (나는 방금 약간의 잘못된 진술을했다; 당신은 그것을 잡았습니까? 그렇지 않으면, 우리는 잠시 후에 그것을 공개 할 것입니다.)
그런 다음 Foo는 고유 한 Task 개체를 만들어 불완전한 것으로 표시 한 다음 스택을 클릭 처리기로 반환합니다.
클릭 핸들러는 Foo의 작업을 검사하고 불완전한 것을 발견합니다. 핸들러에서 대기의 연속은 Bar ()를 호출하는 것이므로 클릭 핸들러는 Bar ()를 호출하는 델리게이트를 작성하고 Foo ()에 의해 리턴 된 태스크의 연속으로 설정합니다. 그런 다음 스택을 메시지 루프로 되돌립니다.
메시지 루프는 메시지 처리를 유지합니다. 결국 지연 작업에 의해 생성 된 타이머 매직은 그 작업을 수행하고 지연 작업의 연속을 이제 실행할 수 있다는 메시지를 큐에 게시합니다. 따라서 메시지 루프는 작업 연속을 호출하여 평소처럼 스택에 배치됩니다. 이 델리게이트는 Blah ()를 호출합니다. Blah ()는 기능을 수행하고 스택을 반환합니다.
이제 어떻게됩니까? 여기 까다로운 부분이 있습니다. 지연 작업의 계속은 Blah () 만 호출 하지 않습니다 . 또한 Bar () 호출을 트리거해야 하지만 해당 작업은 Bar에 대해 알지 못합니다!
Foo는 실제로 (1) Blah ()를 호출하고 (2) Foo가 생성하고 이벤트 처리기로 전달한 작업의 연속을 호출하는 델리게이트를 만들었습니다. 이것이 Bar ()를 호출하는 델리게이트를 호출하는 방법입니다.
그리고 이제 우리는 필요한 모든 것을 올바른 순서로 수행했습니다. 그러나 우리는 메시지 루프에서 메시지 처리를 오랫동안 멈추지 않았으므로 응용 프로그램은 응답 상태를 유지했습니다.
이러한 시나리오가 스택에 비해 너무 진보되어 있다는 것은 완벽한 의미가 있지만 스택을 대체하는 것은 무엇입니까?
대리자의 클로저 클래스를 통해 서로에 대한 참조를 포함하는 작업 개체의 그래프. 이러한 폐쇄 클래스는 가장 최근에 실행 된 대기 위치와 로컬 값을 추적하는 상태 머신 입니다. 또한, 주어진 예에서, 운영 체제에 의해 구현 된 전역 상태 동작 큐 및 그 동작을 실행하는 메시지 루프.
연습 : 메시지 루프가없는 세상에서이 모든 것이 어떻게 작동한다고 생각하십니까? 예를 들어 콘솔 응용 프로그램입니다. 콘솔 앱에서 기다리는 것은 상당히 다릅니다. 지금까지 알고있는 것과 어떻게 작동하는지 추론 할 수 있습니까?
내가 몇 년 전에 알았을 때 스택은 번개가 빠르고 가벼워서 힙에서 멀리 떨어진 응용 프로그램에 할당 된 메모리 조각이기 때문에 현재 작업에 대한 효율적인 관리를 지원했기 때문에 스택이있었습니다. 무엇이 바뀌 었습니까?
스택은 메소드 활성화의 수명이 스택을 형성 할 때 유용한 데이터 구조이지만, 예제에서는 클릭 핸들러 Foo, Bar 및 Blah의 활성화가 스택을 형성하지 않습니다. 따라서 워크 플로를 나타내는 데이터 구조는 스택이 될 수 없습니다. 오히려 워크 플로를 나타내는 힙 할당 작업 및 대리자의 그래프입니다. 대기는 워크 플로에서 이전에 시작된 작업이 완료 될 때까지 워크 플로에서 더 이상 진행할 수없는 지점입니다. 기다리는 동안 완료된 특정 시작 작업에 의존 하지 않는 다른 작업을 실행할 수 있습니다 .
스택은 프레임의 배열 일뿐입니다. 여기서 프레임에는 (1) 함수 중간에 대한 포인터 (호출이 발생한 위치) 및 (2) 로컬 변수 및 온도 값이 포함됩니다. 작업의 계속은 동일합니다 : 델리게이트는 함수에 대한 포인터이며 함수 중간에 특정 지점을 참조하는 상태 (대기 중이 발생한 위치)를 가지며 클로저에는 각 로컬 변수 또는 임시 필드가 있습니다 . 프레임은 더 이상 멋진 깔끔한 배열을 형성하지 않지만 모든 정보는 동일합니다.