비동기 키워드가 필요한 이유는 무엇입니까?


19

방금 .Net 4.5에서 async / await로 놀기 시작했습니다. 내가 처음 궁금한 점은 왜 비동기 키워드가 필요한가? 내가 읽은 설명은 마커가 마커이므로 메소드가 무언가를 기다리는 것을 알고 있습니다. 그러나 컴파일러는 키워드없이 이것을 알아낼 수 있어야합니다. 그럼 또 뭐 해요?


2
다음 은 C #의 키워드와 F #의 관련 기능에 대해 자세히 설명 하는 정말 좋은 블로그 기사 (시리즈)입니다.
paul

나는 그것이 컴파일러가 아니라 주로 개발자를위한 마커라고 생각합니다.
코드 InChaos

8
이 기사에서 설명합니다 : blogs.msdn.com/b/ericlippert/archive/2010/11/11/…
svick

@ svick 덕분에 이것이 내가 찾고있는 것입니다. 이제 완벽하게 이해됩니다.
ConditionRacer

답변:


23

여기에는 몇 가지 답변이 있으며 모두 비동기 메소드의 기능에 대해 이야기하지만 아무도 대답하지 않으므로 async함수 선언에 사용되는 키워드로 필요합니다.

"컴파일러가 특수한 방식으로 함수를 변환하도록 지시하지는 않습니다"; await혼자 할 수 있습니다. 왜? C #에는 메소드 본문에 특수 키워드가 있으면 컴파일러가 메소드 본문에서 극단적 인 (그리고 매우 유사한 async/await) 변환 을 수행하는 또 다른 메커니즘이 이미 있습니다 yield.

즉 제외하고 yieldC #에서 자신의 키워드 아니며, 그 이유를 설명합니다 이해 async뿐만 아니라. 이 메커니즘을 지원하는 대부분의 언어와는 달리, C #으로는 말할 수 없다 yield value;당신은 말을 yield return value;대신. 왜? C #이 존재 한 후에 언어에 추가 되었기 때문에 어딘가에 누군가 yield가 변수의 이름으로 사용했다고 가정하는 것이 합리적 입니다. 그러나 <variable name> return구문 적으로 올바른 기존 시나리오가 없기 때문에 yield return기존 코드와 100 % 이전 버전과의 호환성을 유지하면서 생성기를 도입 할 수 있도록 언어에 추가되었습니다.

그리고 이것이 변수 이름으로 async사용 된 기존 코드가 깨지지 않도록 함수 수정 자로 추가 된 이유 await입니다. async메소드가 이미 존재 하지 않기 때문에 이전 코드가 무효화되지 않고 새 코드에서 컴파일러는 async태그 의 존재를 사용하여 await식별자가 아닌 키워드로 취급되어야 함 을 알 수 있습니다.


3
이것은 이 기사 에서도 언급 한 내용 (원래 의견에 @svick에 게시 됨)이지만 아무도 답으로 게시하지 않았습니다. 2 년 반 밖에 걸리지 않았습니다! 감사합니다 :)
ConditionRacer

그렇다고 향후 버전에서 async키워드 를 지정하기위한 요구 사항을 제거 할 수 있습니까? 분명히 BC 붕괴 일 것이지만, 프로젝트 수에 영향을 미치며 업그레이드하기위한 간단한 요구 사항 (예 : 변수 이름 변경)으로 생각할 수 있습니다.
Quolonel 질문

6

코드 생성을 위해 완전히 다른 접근법이 필요한 콜백이있는 객체로 메소드를 일반 메소드에서 변경합니다.

그런 과감한 일이 발생하면 그것을 명확하게 나타내는 것이 관례입니다 (C ++에서 그 교훈을 배웠습니다)


따라서 이것은 실제로 리더 / 프로그래머를위한 것이지 컴파일러를위한 것이 아닙니다.
ConditionRacer

@ Justin984 그것은 특별한 무언가가 일어날 필요가있는 컴파일러에게 신호를 보내는 데 확실히 도움이됩니다
ratchet freak

컴파일러에 왜 필요한지 궁금합니다. 메소드를 구문 분석하고 메소드 본문 어딘가에 await가 있음을 알 수 없습니까?
ConditionRacer

여러 개의 asyncs를 동시에 예약하고 싶을 때 마다 서로 실행되지 않고 동시에 실행됩니다 (스레드가 사용 가능한 경우)
ratchet freak

@ Justin984 : 반환하는 모든 메소드가 Task<T>실제로 메소드 의 특성을 갖는 것은 아닙니다. async예를 들어, 비즈니스 / 공장 로직을 실행 한 다음 리턴 하는 다른 메소드에 위임 할 수 있습니다 Task<T>. async메소드는 서명Task<T> 에 리턴 유형이 있지만 실제로 a를 리턴 하지는 않고 단지 a를 리턴합니다 . 키워드 없이이 모든 것을 알아 내기 위해 C # 컴파일러는 메소드에 대한 모든 종류의 심층 검사를 수행해야하는데, 이로 인해 속도가 다소 느려질 수 있으며 모든 모호한 방식으로 이어질 수 있습니다. Task<T>Tasync
Aaronaught

4

"비동기"또는 "안전하지 않은"과 같은 키워드를 사용하는 아이디어는 수정 한 코드를 어떻게 처리해야하는지에 대한 모호성을 제거하는 것입니다. async 키워드의 경우 수정 된 메소드를 즉시 리턴 할 필요가없는 것으로 처리하도록 컴파일러에 지시합니다. 이를 통해이 메소드가 사용 된 스레드가 해당 메소드의 결과를 기다리지 않고 계속할 수 있습니다. 효과적으로 코드 최적화입니다.


첫 번째 단락이 정확하지만 인터럽트와의 비교가 정확하지 않기 때문에 공감하고 싶습니다.
paul

나는 그것들을 목적 측면에서 다소 유사하다고 생각하지만 명확성을 위해 그것을 제거 할 것입니다.
월드 엔지니어

인터럽트는 내 생각에 비동기 코드보다 이벤트와 비슷합니다. 정상 코드가 재개되기 전에 컨텍스트 전환이 발생하고 실행이 완료됩니다 (일반 코드도 실행을 완전히 무시할 수 있음). 비동기 코드의 경우 메소드가 호출 스레드가 재개되기 전에 완료되지 않았습니다. 나는 당신이 말하려고하는 것을 보았습니다. 후드 아래에서 다른 구현을 요구하는 다른 메커니즘이 있지만 인터럽트는이 점을 설명하기 위해 저와 함께하지 않았습니다. (나머지는 btw. +1)
paul

0

좋아, 여기 내가 가져 가라.

뭔가라는이 코 루틴 수십 년 동안 알려져있다. ( "Knuth and Hopper"클래스 "수십 년 동안") 이들은 서브 루틴의 일반화입니다 . 예를 들어 함수 시작 및 리턴 명령문에서 제어를 가져오고 해제 할뿐만 아니라 특정 지점 ( 서스펜션 지점 ) 에서도 제어를 수행합니다 . 서브 루틴은 서스펜션 지점이없는 코 루틴입니다.

"프로토스 레드"에 대한 다음 백서에서 볼 수 있듯이 C 매크로로 쉽게 구현할 수 있습니다. ( http://dunkels.com/adam/dunkels06protothreads.pdf ) 읽어보십시오. 기다릴게...

이것의 결론은 매크로가 큰 만들 것입니다 switch, 그리고 case각각의 서스펜션 점에서 레이블을. 각 서스펜션 지점에서이 함수는 다음 case레이블 의 값을 저장 하므로 다음에 호출 할 때 실행을 재개 할 위치를 알 수 있습니다. 그리고 그것은 발신자에게 제어권을 돌려줍니다.

이것은 "프로토스 레드"에 설명 된 코드의 명백한 제어 흐름을 수정하지 않고 수행됩니다.

이 모든 "프로토스 레드"를 차례로 호출하는 큰 루프가 있고 단일 스레드에서 "프로토스 레드"를 동시에 실행한다고 상상해보십시오.

이 방법에는 두 가지 단점이 있습니다.

  1. 재개 사이의 로컬 변수 상태를 유지할 수 없습니다.
  2. 임의의 호출 깊이에서 "프로토스 레드"를 일시 중단 할 수 없습니다. (모든 서스펜션 포인트는 레벨 0이어야합니다)

두 가지 모두에 대한 해결 방법이 있습니다.

  1. 모든 로컬 변수는 프로토스 레드의 컨텍스트로 풀업되어야합니다 (프로토스 레드는 다음 재개 지점을 저장해야한다는 사실에 의해 이미 필요한 컨텍스트)
  2. 실제로 프로토스 레드에서 다른 프로토스 레드를 호출해야한다고 생각되면 자식 프로토스 레드를 "스폰"하고 자식이 완료 될 때까지 일시 중단합니다.

매크로와 해결 방법이 수행하는 재 작성 작업을 수행하도록 컴파일러를 지원했다면 의도 한대로 프로토스 레드 코드를 작성하고 키워드로 서스펜션 포인트를 삽입 할 수 있습니다.

그리고 이것은 무엇 asyncawait생성 (스택리스) 코 루틴 : 모든 약이 있습니다.

C #의 코 루틴은 (generic 또는 non-generic) class의 객체로 구체화됩니다 Task.

이러한 키워드는 매우 오해의 소지가 있습니다. 내 정신 독서는 다음과 같습니다

  • async "필수"로
  • await "완료까지 일시 중단"으로
  • Task "미래…"

지금. 함수를 표시해야 async합니까? 함수를 코 루틴으로 만들기 위해 코드 다시 작성 메커니즘을 트리거해야한다는 말과는 별도로 일부 모호성을 해결합니다. 이 코드를 고려하십시오.

public Task<object> AmIACoroutine() {
    var tcs = new TaskCompletionSource<object>();
    return tcs.Task;
}

async필수가 아니라고 가정하면 , 이것이 코 루틴입니까 아니면 정상적인 기능입니까? 컴파일러가이를 코 루틴으로 다시 작성해야합니까? 둘 다 다른 의미론으로 가능할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.