먼저 친절한 말씀 감사합니다. 정말 멋진 기능이며 그 일부가 된 것이 기쁩니다.
내 모든 코드가 천천히 비 동기화되는 경우 기본적으로 모든 코드를 비 동기화하지 않는 이유는 무엇입니까?
글쎄, 당신은 과장하고 있습니다. 모든 코드가 비동기로 바뀌지 않습니다. 두 개의 "일반"정수를 더하면 결과를 기다리지 않습니다. 미래 의 세 번째 정수 를 얻기 위해 두 개의 미래 정수 를 더할 때 - 그것이 바로 미래에 접근하게 될 정수 이기 때문입니다 Task<int>
-물론 결과를 기다리고있을 것입니다.
모든 것을 비 동기화하지 않는 주된 이유 는 async / await의 목적이 대기 시간이 긴 작업이 많은 세계에서 코드를 더 쉽게 작성할 수 있도록하기 때문 입니다. 대부분의 작업은 지연 시간이 길지 않으므로 지연 시간을 완화하는 성능 저하를 감수하는 것은 의미가 없습니다. 오히려 주요 작업은 대기 시간이 길고 이러한 작업으로 인해 코드 전체에서 좀비가 비 동기화됩니다.
성능이 유일한 문제인 경우 일부 영리한 최적화는 필요하지 않을 때 자동으로 오버 헤드를 제거 할 수 있습니다.
이론적으로 이론과 실제는 비슷합니다. 실제로는 그렇지 않습니다.
이런 종류의 변환과 최적화 과정에 대한 세 가지 점을 알려 드리겠습니다.
첫 번째 요점은 C # / VB / F #의 비동기는 본질적으로 제한된 형태의 연속 전달 입니다. 함수형 언어 커뮤니티에서 엄청난 양의 연구를 통해 연속 전달 스타일을 많이 사용하는 코드를 최적화하는 방법을 파악하는 방법을 알아 냈습니다. 컴파일러 팀은 "비동기"가 기본값이고 비 비동기 메서드를 식별하고 비 동기화해야하는 세계에서 매우 유사한 문제를 해결해야 할 것입니다. C # 팀은 공개 된 연구 문제를 다루는 데별로 관심이 없으므로 바로 거기에 대한 큰 포인트입니다.
반대의 두 번째 요점은 C #에는 이러한 종류의 최적화를보다 다루기 쉽게 만드는 "참조 투명성"수준이 없다는 것입니다. "참조 투명성"이란 식의 값이 평가 될 때 의존하지 않는 속성을 의미합니다 . 다음과 같은 표현식 2 + 2
은 참조 적으로 투명합니다. 원하는 경우 컴파일 타임에 평가를 수행하거나 런타임까지 연기하고 동일한 답변을 얻을 수 있습니다. 그러나 x와 y가 시간이 지남에 따라 변할 수x+y
있기 때문에 다음 과 같은 표현식 은 시간에 따라 움직일 수 없습니다 .
비동기를 사용하면 부작용이 언제 발생하는지 추론하기가 훨씬 더 어려워집니다. 비동기 전에 다음과 같이 말한 경우 :
M();
N();
과 M()
했다 void M() { Q(); R(); }
, 그리고 N()
이었다 void N() { S(); T(); }
, 그리고 R
및 S
생산 부작용, 당신은 R의 부작용 S의 부작용 전에 일어나는 것을 알고있다. 그러나 만약 async void M() { await Q(); R(); }
그렇다면 갑자기 그것은 창 밖으로 나갑니다. (물론 기다리지 않는 한, R()
그 전후에 일어날 것인지 보장 할 수 없습니다 . 물론 그 이후까지 기다릴 필요는 없습니다 .)S()
M()
Task
N()
이제 어떤 순서의 부작용이 발생하는지 더 이상 알지 못하는 이 속성이 최적화 프로그램이 비 동기화 해제를 관리하는 코드를 제외하고 프로그램의 모든 코드에 적용 된다고 상상해보십시오 . 기본적으로 어떤식이 어떤 순서로 평가되는지 더 이상 알 수 없습니다. 즉, 모든식이 참조 적으로 투명해야하므로 C #과 같은 언어에서는 어렵습니다.
반대의 세 번째 요점은 "비동기가 왜 그렇게 특별한가?"라는 질문을해야한다는 것입니다. 모든 작업이 실제로 이루어져야한다고 주장 Task<T>
하려면 "왜 안 Lazy<T>
되는가?"라는 질문에 답할 수 있어야합니다. 또는 "왜 안돼 Nullable<T>
?" 또는 "왜 안돼 IEnumerable<T>
?" 우리는 그렇게 쉽게 할 수 있기 때문입니다. 모든 작업이 nullable로 해제 되는 경우가 아닌 이유는 무엇 입니까? 또는 모든 작업이 느리게 계산되고 결과가 나중에 캐시 되거나 모든 작업의 결과가 단일 값이 아닌 일련의 값 입니다. 그런 다음 "아, 이것은 null이 아니어야합니다. 따라서 더 나은 코드를 생성 할 수 있습니다"등을 알고있는 상황을 최적화해야합니다.
요점은 Task<T>
이 정도의 작업을 보증 하는 것이 실제로 그렇게 특별 하다는 것이 나에게 명확하지 않다는 것입니다.
이러한 종류에 관심이 있다면 참조 투명성이 훨씬 더 강하고 모든 종류의 비 순차적 평가를 허용하고 자동 캐싱을 수행하는 Haskell과 같은 기능 언어를 조사하는 것이 좋습니다. Haskell은 또한 내가 언급 한 "모나드 리프팅"의 종류에 대한 유형 시스템에서 훨씬 더 강력한 지원을 제공합니다.