iOS/Swift

[Swift] Concurrency [2]

최지철 2023. 10. 25. 16:06
728x90
반응형

어싱크가 아니라 에이싱크라니?!


async & await

  • async: 비동기 함수임을 나타낸다.
  • await: async 키워드가 표시된 메소드나 함수의 리턴을 기다린다. 즉, async 함수는 비동기적으로 동작할 수 있고, await 키워드를 사용해 비동기 함수의 결과를 대기할 수 있습니다.
func loadWebResource(_ path: String) async throws -> Resource
func decodeImage(_ r1: Resource, _ r2: Resource) async throws -> Image
func dewarpAndCleanupImage(_ i : Image) async throws -> Image

func processImageData() async throws -> Image {
  let dataResource  = try await loadWebResource("dataprofile.txt")
  let imageResource = try await loadWebResource("imagedata.dat")
  let imageTmp      = try await decodeImage(dataResource, imageResource)
  let imageResult   = try await dewarpAndCleanupImage(imageTmp)
  return imageResult
}

completion handler가 사라지면서, 비동기 함수를 한 줄로 처리할 수 있어 훨씬 간결해 보인다. 이와 동시에 retain cycle이 발생할 우려도 사라졌다. 또, 개발자가 실수로 try await 키워드를 빼먹은 경우에는 컴파일러가 오류를 알려줘 조금 더 안정적인 에러 핸들링이 가능하다.

 

동작원리 

  • sync에서의 스레드 관리
    • 호출 - A함수에서 B함수(sync)를 호출하면, A함수가 실행되던 스레드의 제어권을 B에게 전달
    • 진행 - B함수가 끝날때까지 해당 스레드는 점유되어서 다른 일을 수행하지 않게되는 원리
    • 종료 - B함수가 종료되면 A함수에게 다시 스레드 제어권을 반납

  • async에서의 스레드 관리
    • 호출 - A함수에서 B함수(async)를 호출하면, A함수가 실행되던 스레드의 제어권을 B에게 전달 
    • 진행 - B함수는 async이기 때문에 스레드의 제어권을 포기하는 suspend가 가능 (suspend되면 호출한 A함수도 같이 suspend됨)
    • suspend - 스레드에 대한 제어권은 system으로 가고 시스템은 스레드를 사용하여 다른 작업을 수행
    • resume - 일시 중단된 비동기 함수 B를 다시 실행하는 단계
    • 종료 - B함수가 종료되면 A함수에게 스레드 제어권을 반납

그렇다면, 비동기 처리 할때, suspend와 resume이 왜 필요할까?

  • Swift의 비동기 함수는 여러 task의 집합으로 이루어 져있다. 동기 함수가 다른 비동기 함수를 호출했을 때, 해당 호출 또한 task이다. task는 동기 함수에서 스레드 내 함수 컨텍스트에 해당하는 continuation을 포함한다.
  • continuation은 task가 suspend 되었을 때 발생하며, resume 될 때 이를 이용해 suspension point로 돌아갈 수 있다.
  • 함수가 실행되는 과정에서 await 키워드를 만나면 해당 지점을 suspension point로 표시하게 된다.
  • 작업이 suspend되면, task는 작업을 진행 중이던 스레드의 제어권을 포기하게 된다. 이때 해당 스레드 제어권은 시스템이 가지게 되는데, 시스템은 이 스레드에서 다른 작업을 수행하다가 task의 수행이 필요하다고 느껴지는 적절한 시점에 스레드 제어권을 반환한다.
  • 스레드 제어권은 다시 suspension point가 발생한 지점으로 돌아가 작업이 resume된다. 
✅ suspend → resume의 과정
    1. await 키워드를 만나면 suspension point로 지정하고 일시정지 (suspend)
    2. 스레드의 제어권을 시스템에게 넘겨줌
    3. 시스템이 다시 비동기 함수에게 스레드 제어권을 넘겨줌
    4. suspension point에서 작업 재개 (resume)

continuation 이란?

  • 특정 지점에서의 함수 실행 컨텍스트를 추적할 수 있는 객체
  •  Swift 동시성 모델에서는 각 task에 대해서 스레드를 생성하는 대신 continuation을 할당
  • heap에 저장되기 때문에 스레드 간의 함수 컨텍스트를 공유할 수 있다.
  • 미래에 사용될 가능성이 있는 변수들이 continuation의 형태로 heap에 저장
  • uspend 되었던 함수가 resume 되면, stack의 최상단 frame이 해당 함수 frame으로 교체된다. 이미 heap에 함수 컨텍스트가 저장되어 있기 때문에 새로운 stack frame을 생성하지 않고 교체만으로도 동작할 수 있다.

요약

  • 비동기 함수가 suspend 되면 해당 스레드 제어권은 시스템이 가지고 있기 때문에, 시스템 판단하에 적절한 스레드를 찾아서 task에게 제어권을 넘겨준다. 여기서 말하는 적절한 스레드는 ‘원래 작업 중이던 스레드’ 혹은 ‘작업이 다 완료되어 쉬고 있는 다른 스레드’가 될 수 있습니다. 따라서 작업이 resume 되는 스레드가 같지 않을 수 있다는 특징이 여기서 발생하게 된다 . 이말은 곧, 비동기 함수를 실행하다가 UI 업데이트 같은 중요한 작업을 진행할 수 있게 해준다는 뜻이 된다.
continuation 특징
    ∙ 컨텍스트 스위칭을 함수 호출로 대체 → 스케줄링 오버헤드 감소
    ∙ 추가적인 스레드 생성 없이 스레드 재사용 → thread explosion 방지
Swift Concurrency에 적용된  변화
    📌 언어적 개선
        ∙ completion handler 제거로 인한 가독성 증가
        ∙ self 참조 사이클이 생길 우려 제거
        ∙ 에러 처리 용이
    📌 성능적 개선
        ∙ 시스템이 스레드 관리
        ∙ continuation으로 스레드 증가 방지
        ∙ 컨텍스트 스위칭을 함수 호출로 대체
728x90
반응형