프로그래밍시스템 프로그래머

러스트에서 비동기성 및 Future 작업은 어떻게 구성되어 있나요? async/await의 구현에 대한 독특한 특징은 무엇이며, 다른 언어의 유사한 메커니즘과 어떻게 다릅니까?

Hintsage AI 어시스턴트로 면접 통과

답변

러스트에서 비동기성은 Future를 통해 구현됩니다. 이는 미래에 결과를 얻을 작업을 나타내는 객체입니다. async fn으로 선언된 함수는 Future 트레이트를 구현하는 익명 구조체 생성기를 반환합니다. 비동기 코드를 실행하려면 런타임(예: Tokio 또는 async-std)이 필요합니다. 표준 라이브러리에는 내장된 이벤트 루프가 포함되어 있지 않습니다.

러스트의 주요 특징:

  • 제로 비용 추상화 — 컴파일러는 비동기 코드를 상태 기계로 변환하며, 작업이 허용하는 경우에는 힙에서의 할당이 없습니다.
  • 네이티브 가비지 컬렉터 없음 — 모든 자원은 명시적으로 관리되며, 이는 라이프타임 및 소유권에 대한 특별한 주의를 요구합니다.
  • Send/Sync — 스레드 간 작업의 이동성과 동기화에 대한 제약이 있습니다.

예:

use tokio::time::sleep; use std::time::Duration; async fn foo() { println!("안녕하세요"); sleep(Duration::from_secs(1)).await; println!("세계!"); } #[tokio::main] async fn main() { foo().await; }

함정 질문

러스트에서 비동기 함수는 기본적으로 호출 시 즉시 실행될 수 있나요? 그 이유는 무엇인가요?

답변: 아니요. async fn 호출은 결과를 반환하는 것이 아니라 Future(상태 기계) 유형의 객체를 반환합니다. 실제 실행을 위해서는 .await를 호출하거나 Future를 런타임에 전달해야 합니다. 호출 자체는 작업의 설명만 생성할 뿐, 실제로 수행하지는 않습니다.

예:

async fn answer() -> u32 { 42 } let fut = answer(); // 여기서는 실행되지 않고 future만 생성됩니다. let result = fut.await; // 여기서 실행이 시작됩니다.

주제의 미묘한 사항에 대한 실제 오류 예


이야기

하이로드 백엔드 프로젝트에서 주니어 개발자는 여러 async 함수를 선언했지만 아무 데서도 .await를 호출하지 않았습니다. 이로 인해 주요 스레드가 동기적으로 실행되어 성능 저하와 응답 시간이 3배 증가했습니다.

이야기

마이크로서비스에서 tokio를 통해 비동기 API를 사용했습니다. 이식 중 개발자는 async-std와 tokio를 동시에 사용하려고 시도했지만 이벤트 루프는 하나만 있어야 한다는 것을 잊었습니다. 그 결과 철회 및 런타임 팬닉이 발생했습니다. 두 런타임이 충돌했기 때문입니다.

이야기

팀의 한 구성원이 Future 내에서 사용되는 유형에 대한 Send/Sync 제약을 잊었습니다. 스레드 간에 future를 공유하려 할 때 애플리케이션이 특정 구조체에 대해 Send를 구현해야 한다는 컴파일 오류가 발생하여 상태 저장 아키텍처를 재검토해야 했습니다.