Swift 정리 - Concurrency

김세영·2022년 3월 31일
0

Swift Doc 정리

목록 보기
17/17
post-thumbnail

Swift는 구조화된 방식으로 비동기와 병렬 코드 작성을 지원

  • 비동기(Asynchronous) 코드
    UI 업데이트를 진행하면서 네트워크 작업 또는 파일 분석 등을 계속 진행
  • 병렬(Parallel) 코드
    멀티코어 프로세서에서 각 코어가 일정 코드를 동시에 실행

동시성과 스레드 작업

  • Swift의 동시성 모델은 스레드의 최상단에 위치하지만 직접 상호작용하지는 않음
  • 비동기 함수는 실행중인 스레드를 포기 가능
    • 포기 시 첫 번째 함수가 차단되는 동안 해당 스레드에서 다른 비동기 작업을 실행 가능

클로저를 이용해 비동기 작업을 진행할 수 있지만, 코드에 클로저가 많아지면 다루기 어려워지므로 동시성에 대한 언어 지원을 사용하여 해결 가능

Defining and Calling Asynchronous Functions

비동기 함수는 실행 도중에 일시적을 중단될 수 있는 특수한 함수

  • 완료, 오류 발생, 오류 반환이 일어나는 일반적인 함수와 대조
  • 3가지 중 하나를 수행하지만 특정 작업을 기다리고 있을 때 일시 정지될 수 있음
  • 비동기 함수임을 나타내기 위해 선언 시 async 키워드를 작성
    func asyncFunction(...) [async] [throws] [-> Value]
    비동기 함수를 호출하면 해당 메서드가 반환될 때 까지 실행이 정지
  • 실행이 정지될 부분은 await 키워드로 작성
  • 비동기 함수 내에서의 실행 흐름은 다른 비동기 함수를 호출할 때만 중단
    • 중단은 암시적이거나 선점적이지 않음
func downloadFirstPhoto() async -> Photo {
  let photoNames = await listPhotos(inGallery: "Summer Vacation")
  let sortedNames = photoNames.sorted()
  let name = sortedNames[0]
  let photo = await downloadPhoto(named: name)
  return photo
}
  1. 함수 실행 후 첫 번째 await까지 진행, listPhotos(inGallary:)가 반환될 때 까지 실행 중단
  2. 중단되는 동안 프로그램의 다른 코드가 실행
  3. listPhotos(inGallary:)가 반환되면 코드를 이어서 실행
  4. 다음 awaitdownloadPhoto(named:)가 반환될 때 까지 실행 중단
  5. 중단되는 동안 프로그램의 다른 코드가 실행
  6. downloadPhoto(named:)가 반환되면 photo를 반환

await를 만나면 Swift가 현재 스레드에서 다른 코드를 실행하기 때문에 스레드 양보(yielding the thread)라고 부름

await는 코드의 실행을 중단할 수 있어야 하므로 프로그램의 특정 위치에서만 비동기 함수를 호출 가능

  • 비동기 함수, 프로퍼티의 바디에 있는 코드
  • @main으로 표시된 구조체, 클래스
  • enum의 정적 main() 메서드에 있는 코드
  • 구조화되지 않은 하위 Task의 코드

Task.sleep(_:)

아무런 동작도 하지 않고 주어진 나노 단위의 초만큼 대기
네트워크 작업 등 시간이 오래 걸리는 작업을 테스트할 때 유용


Asynchronous Sequences

비동기 시퀀스로 한 번에 컬렉션의 한 element를 기다리는 것

  • for-in 대신 for-await-in을 사용
  • 다음 element를 사용할 수 있을 때 까지 기다림
  • 각 반복이 시작될 때 잠재적으로 실행을 중단

Calling Asynchoronous Functions in Parallel

let firstTask = await longTask(order: 1)
let secondTask = await longTask(order: 2)
let thirdTask = await longTask(order: 3)

let tasks = [firstTask, secondTask, thirdTask]

위 코드는 하나의 longTask(order:)이 완료되기 전 까지 다음 작업이 불가능하지만, 동시에 3개의 작업을 수행하면 매우 효율적

async let firstTask = longTask(order: 1)
async let secondTask = longTask(order: 2)
async let thirdTask = longTask(order: 3)

let tasks = await [firstTask, secondTask, thirdTask]

위 코드는 각 longTask(order:)await이 없기 때문에 각각의 작업이 완료될 때 까지 실행을 중단하지 않음

  • 마지막 await에서 3개의 작업을 동시에 수행
  • 코드에서 비동기 작업의 결과가 다음 작업에 영향을 주지 않는다면 async-let을 사용

Tasks, Task Groups

Task는 비동기적으로 실행할 수 있는 작업 단위
Task GroupTask를 추가하면 우선순위와 취소를 쉽게 제어할 수 있으며, 동적으로 작업의 수를 제어 가능

  • Task는 계층 구조로 정렬
  • TaskGroup의 각 Task에는 동일한 상위 Task가 있으며, 각 Task는 하위 Task가 있을 수 있다.
  • 이러한 명시적 관계 때문에 구조적 동시성이라고 함
  • 명시적 관계를 통해 취소 전파와 같은 동작을 처리 가능
    • 컴파일 타임에 일부 오류 감지 가능

TaskGroup - Apple Developer

Unstructured Concurrency

TaskGroup과 달리 상위 작업이 존재하지 않음

  • 구조화되지 않는 유연성이 있지만 정확성에 대한 완전한 책임이 존재
  • Task.init(priority:operation:)을 호출해 현재 actor에서 실행될 Unstructured Task를 생성
  • Task.detached(priority:operation:)을 호출해 현재 actor의 일부가 아닌 Unstructured Task를 생성
  • 두 가지 모두 Task Handle을 반환

Task Cancellation

Swift Concurrency는 협동 취소 모델(cooperative cancellation model)을 사용

  • 각 작업은 적절한 시점에서 취소되었는지 확인, 적절한 방법으로 취소에 응답
    • CancellationError 등의 에러 발생
    • nil 또는 [] 반환
    • 부분적으로 완료된 작업 반환
  • Task.checkCancellation() 또는 Task.isCancelled로 취소를 확인
  • Task.cancel()로 수동으로 취소

Actors

actor은 클래스와 마찬가지로 참조 타입

  • 한 번에 하나의 작업만, 변경 가능한 상태에 접근할 수 있도록 허용
  • 여러 작업의 코드가 actor의 동일한 인스턴스에 접근하는 것에 대한 안전을 보장
  • actor의 외부에서 await 없이 프로퍼티에 접근 불가능
    • 내부에서만 로컬 상태에 접근할 수 있도록 허용
profile
초보 iOS 개발자입니다ㅏ

0개의 댓글