NOTE 이전에 동시성 코드를 작성한 적이 있다면 쓰레드 동작에 익숙할 것입니다. Swift 에서 동시성 모델은 쓰레드의 최상단에 구축되지만 직접적으로 상호작용하지 않습니다. Swift 에서 비동기 함수는 실행중인 쓰레드를 포기할 수 있습니다. 그러면 첫번째 함수가 차단되는 동안 해당 쓰레드에서 다른 비동기 함수가 실행될 수 있습니다.
throws
사용하는 것과 유사하게 파라미터 뒤의 선언에 async
키워드를 작성합니다. 함수 또는 메서드가 값을 반환한다면 반환 화살표 (->
) 전에 async
를 작성합니다. 예를 들어 갤러리에 사진의 이름을 가져오는 방법은 아래와 같습니다:throws
전에 async
를 작성합니다.await
를 작성합니다. 이것은 에러가 있는 경우 프로그램의 흐름을 변경 가능함을 나타내기 위해 던지는 함수를 호출할 때 try
를 작성하는 것과 같습니다. 비동기 메서드 내에서 실행 흐름은 다른 비동기 메서드를 호출할 때만 일시 중단됩니다—중단은 암시적이거나 선점적이지 않습니다—이것은 가능한 모든 중단 지점이 await
로 표시된다는 의미입니다.listPhotos(inGallery:)
와 downloadPhoto(named:)
함수 모두 네트워크 요청을 필요로 하기 때문에 완료하는데 비교적 오랜시간이 걸릴 수 있습니다. 반환 화살표 전에 async
를 작성하여 둘 다 비동기로 만들면 이 코드는 그림이 준비될 때까지 기다리는 동안 앱의 나머지 코드가 계속 실행될 수 있습니다.await
까지 실행됩니다. listPhotos(inGallery:)
함수를 호출하고 해당 함수가 반환될 때까지 실행을 일시 중단합니다.await
로 표시된 다음 중단 지점 또는 완료될 때까지 실행됩니다.listPhotos(inGallery:)
가 반환된 후에 이 코드는 해당 지점에서 시작하여 계속 실행됩니다. 반환된 값을 photoNames
에 할당합니다.sortedNames
와 name
을 정의하는 라인은 일반적인 동기 코드 입니다. 이 라인은 await
로 표시되지 않았으므로 가능한 중단 지점이 없습니다.await
는 downloadPhoto(named:)
함수에 대한 호출을 표시합니다. 이 코드는 해당 함수가 반환될 때까지 실행을 다시 일시 중단하여 다른 동시 코드에 실행할 기회를 제공합니다.downloadPhoto(named:)
가 반환된 후에 반환값은 photo
에 할당된 다음에 show(_:)
를 호출할 때 인수로 전달됩니다.await
로 표시된 코드의 중단이 가능한 지점은 비동기 함수 또는 메서드가 반환되기를 기다리는 동안 현재 코드 부분이 실행을 일시적으로 중단할 수 있음을 나타냅니다. Swift 가 현재 쓰레드에서 코드의 실행을 일시 중단하고 대신 해당 쓰레드에서 다른 코드를 실행하기 때문에 이것을 쓰레드 양보 (yielding the thread) 라고도 부릅니다. await
가 있는 코드는 실행을 일시 중단할 수 있어야 하므로 프로그램의 특정 위치에서만 비동기 함수 또는 메서드를 호출할 수 있습니다:@main
으로 표시된 구조체, 클래스, 또는 열거형의 정적 (static) main()
메서드에 있는 코드add(_:toGallery:)
와 remove(_:fromGallery:)
사이에 실행되는 다른 코드는 없습니다. 그 시간동안 첫번째 사진은 양쪽 갤러리에 모두 나타나며 앱의 불변성 중 하나를 임시적으로 위반합니다. 이 코드에 await
가 확실히 추가되지 말아야 한다는 것을 나타내기 위해 동기 함수로 코드를 리팩토링 할 수 있습니다:move(_:from:to:)
함수는 동기 함수기 때문에 중단 가능한 지점을 포함히지 않는다는 것을 보장할 수 있습니다. 이 함수에 중단 가능한 지점을 도입하기위해 비동기 코드를 추가하면 버그가 아닌 컴파일 에러가 발생합니다.NOTETask.sleep(until:clock:)
메서드는 동시성 작동 방식을 배우기 위해 간단한 코드를 작성할 때 유용합니다. 이 메서드는 아무런 동작도 하지 않지만 반환되기 전에 주어진 나노 단위의 초만큼 기다립니다. 다음은 네트워크 작업 대기를 시뮬레이션하기 위해sleep(until:clock:)
을 사용하는listPhoto(inGallery:)
함수의 버전입니다:1func listPhotos(inGallery name: String) async throws -> [String] {2try await Task.sleep(until: .now + .seconds(2), clock: .continuous)3return ["IMG001", "IMG99", "IMG0404"]4}Copied!
listPhotos(inGallery:)
함수는 비동기적으로 배열의 모든 요소가 준비된 후에 전체 배열을 한번에 반환합니다. 또 다른 접근 방식은 비동기 시퀀스 (asynchronous sequence) 를 사용하여 한번에 콜렉션의 한 요소를 기다리는 것입니다. 비동기 시퀀스에 대한 조회 동작은 다음과 같습니다:for
-in
루프 대신에 위의 예제는 for
다음에 await
를 작성합니다. 비동기 함수 또는 메서드 호출할 때와 마찬가지로 await
작성은 가능한 중단 지점을 나타냅니다. for
-await
-in
루프는 다음 요소를 사용할 수 있을 때까지 기다리고 각 반복이 시작될 때 잠재적으로 실행을 일시 중단합니다.Sequence
프로토콜에 준수성을 추가하여 for
-in
루프에서 자체 타입을 사용할 수 있는 것과 같은 방식으로 AsyncSequence
프로토콜에 준수성을 추가하여 for
-await
-in
루프에서 자체 타입을 사용할 수 있습니다.await
를 사용하여 비동기 함수를 호출하면 한번에 코드의 한 부분만 실행됩니다. 비동기 코드가 실행되는 동안 호출자는 코드의 다음 라인을 실행하기 위해 이동하기 전에 해당 코드가 완료될 때까지 기다립니다. 예를 들어 갤러리에서 처음 세 장의 사진을 가져오려면 다음과 같이 downloadPhoto(named:)
함수에 대한 세 번의 호출을 기다릴 수 있습니다:downloadPhoto(named:)
에 대한 호출은 한 번에 하나만 실행됩니다. 각 사진은 다음 사진이 다운로드를 시작하기 전에 완료됩니다. 그러나 이런 작업을 기다릴 필요가 없습니다—각 사진은 개별적으로 또는 동시에 다운로드 할 수 있습니다.let
앞에 async
를 작성하고 상수를 사용할 때마다 await
를 작성합니다.downloadPhoto(named:)
을 호출하는 세가지는 모두 이전 호출이 완료되길 기다리지 않고 시작됩니다. 사용할 수 있는 시스템 자원이 충분하다면 동시에 실행할 수 있습니다. 코드가 함수의 결과를 기다리기 위해 일시 중단되지 않기 때문에 이러한 함수 호출 중 어느 것도 await
로 표시하지 않습니다. 대신 photos
가 정의된 라인까지 실행이 계속됩니다—이 시점에서 프로그램은 이러한 비동기 호출의 결과를 필요로 하므로 세 장의 사진이 모두 다운로드 될 때까지 실행을 일시 중단하기 위해 await
를 작성합니다.await
를 사용하여 비동기 함수를 호출합니다. 이것은 순차적으로 실행되는 작업을 생성합니다.async
-let
을 사용하여 비동기 함수를 호출합니다. 이렇게 하면 병렬로 수행할 수 있는 작업이 생성됩니다.await
와 async
-let
은 모두 일시 중단되는 동안 다른 코드를 실행할 수 있도록 합니다.await
로 표시합니다.async
-let
구문은 하위 작업을 생성합니다. 작업 그룹 (task group) 을 생성하고 해당 그룹에 하위 작업을 추가할 수도 있습니다. 그러면 우선순위와 취소를 더 잘 제어할 수 있으며 동적으로 작업의 수를 생성할 수 있습니다.Task.init(priority:operation:)
초기화 구문을 호출해야 합니다. 더 구체적으로 분리된 작업으로 알려진 현재 행위자의 일부가 아닌 구조화되지 않은 작업을 생성하려면 Task.detached(priority:operation:)
클래스 메서드를 호출합니다. 이 모든 동작은 서로 상호작용 할 수 있는 작업 (task)을 반환합니다—예를 들어 결과를 기다리거나 취소하는 경우가 해당됩니다.CancellationError
와 같은 에러 발생nil
또는 빈 콜렉션 반환CancellationError
를 발생시키는 Task.checkCancellation()
을 호출하거나 Task.isCancelled
의 값을 확인하고 자체 코드에서 취소를 처리합니다. 예를 들어 갤러리에서 사진을 다운로드 하는 작업은 일부 다운로드를 삭제하고 네트워크 연결을 닫아야 할 수 있습니다.actor
키워드를 사용하여 행위자를 도입하고 중괄호로 정의합니다. TemperatureLogger
행위자는 행위자 외부의 다른 코드가 접근할 수 있는 프로퍼티가 있으며 행위자 내부의 코드만 최대값을 업데이트 할 수 있게 max
프로퍼티를 제한합니다.await
를 사용합니다. 예를 들어:logger.max
에 접근하는 것은 일시 중단 지점으로 가능합니다. 행위자는 한 번에 하나의 작업만 변경 가능한 상태에 접근할 수 있도록 허용하므로 다른 작업의 코드가 이미 로거와 상호 작용하고 있는 경우 이 코드는 프로퍼티 접근을 기다리는 동안 일시 중단됩니다.await
를 작성하지 않습니다. 예를 들어 새로운 온도로 TemperatureLogger
를 업데이트 하는 메서드 입니다:update(with:)
메서드는 행위자에서 이미 실행 중이므로 max
와 같은 프로퍼티에 대한 접근을 await
로 표시하지 않습니다. 이 메서드는 행위자가 변경 가능한 상태와 상호 작용하기 위해 한 번에 하나의 작업만 허용하는 이유 중 하나를 보여줍니다: 행위자의 상태에 대한 일부 업데이트는 일시적으로 불변성을 깨뜨립니다. TemperatureLogger
행위자는 온도 목록과 최대 온도를 추적하고 새로운 측정값을 기록할 때 최대 온도를 업데이트 합니다. 업데이트 도중에 새로운 측정값을 추가한 후 max
를 업데이트 하기 전에 온도 로거는 일시적으로 일치하지 않는 상태가 됩니다. 여러 작업이 동일한 인스턴스에 상호 작용하는 것을 방지하면 다음 이벤트 시퀀스와 같은 문제를 방지할 수 있습니다:update(with:)
메서드를 호출합니다. 먼저 measurements
배열을 업데이트 합니다.max
를 업데이트 하기 전에 다른 코드에서 최대값과 온도 배열을 읽습니다.max
를 변경하여 업데이트를 완료합니다.update(with:)
호출 중간에 행위자에 대한 접근이 인터리브 (interleaved) 되어 잘못된 정보를 읽습니다. Swift 행위자는 한 번에 해당 상태에 대해 하나의 작업만 허용하고 해당 코드는 await
가 일시 중단 지점으로 표시되는 위치에서만 중단될 수 있기 때문에 Swift 행위자를 사용하여 이 문제를 방지할 수 있습니다. update(with:)
는 일시 중단 지점을 포함하지 않으므로 다른 코드는 업데이트 중간에 데이터에 접근할 수 없습니다.await
작성 없이 logger.max
에 접근하는 것은 행위자의 프로퍼티가 해당 행위자의 분리된 로컬 상태의 부분이기 때문에 실패합니다. Swift 는 행위자 내부의 코드만 행위자의 로컬 상태에 접근할 수 있도록 보장합니다. 이 보장을 행위자 분리 (actor isolation) 이라고 합니다.Sendable
프로토콜을 선언하여 전송 가능한 타입으로 표시합니다. 이 프로토콜은 어떠한 코드 요구사항을 가지지 않지만 Swift 가 적용하는 의미론적 요구사항이 있습니다. 일반적으로 타입을 전송 가능한 것으로 나타내기 위한 세가지 방법이 있습니다:@MainActor
로 표시된 클래스나 특정 쓰레드나 큐에서 프로퍼티에 순차적으로 접근하는 클래스와 같이 변경 가능한 상태의 안정성을 보장하는 코드를 가지고 있습니다.TemperatureReading
은 전송 가능한 프로퍼티만 가지는 구조체이며 public
또는 @usableFromInline
으로 표시되지 않은 구조체이므로 암시적으로 전송 가능합니다. 다음은 Sendable
프로토콜을 준수가 암시되는 구조체입니다: