brunch

You can make anything
by writing

C.S.Lewis

by 이응 Jan 04. 2024

Swift의 async/await에 대해(동기,비동기)

동기 비동기 참조 이미지


동기(Sync)

동기는 단어 그대로 동시에 일어난다는 의미인데, 이는 작업이 전부 동시에 일어난다는 뜻이 아니라 "요청과 응답"이 동시에 일어난다는 뜻이다.


즉, 카페를 예시로 들면 주인이 카운터에서 주문을 받자 해당 커피를 곧장 제작을 해서 손님에게 주는 방식이라고 볼 수 있다. 따라서 해당 카페는 손님이 많이 올 수록 주문이 점점 밀릴 수 밖에 없다.


일반적으로 코드를 작성하게 되면 이러한 동기 방식으로 작동하기 때문에 코드 작성 순서대로 호출되고 결과가 출력된다.



비동기(Async)

 "요청과 응답"이 동시에 일어나지 않는 방식으로 요청하면 상황에 따라서 뒤늦게 응답되거나 아예 응답되지 않을 수도 있다.


위와 같이 카페로 예를 들자면 사장이 주문을 받자 바리스타들이 커피 제작을 진행하게 된다. 다만 커피 종류에 따라 걸리는 시간 차가 있어 먼저 온 손님의 커피가 뒤에 온 손님보다 늦게 나올 수도 있는 상황과 유사하다고 볼 수 있다.



Async/await 사용해보기

async는 함수가 비동기로 처리된다는 것을 의미한다 async 키워드는 함수 명 오른쪽에 표시해야 한다

throws를 같이 붙여야 할 때는 async throws로 쓰면 된다.


async 함수를 호출하기 위해서는 await 키워드가 필요하다 throws와 try가 한 쌍인 것처럼 async와 await가 한 쌍이라고 생각하면 된다 await로 마킹된 곳은 potential suspension point(잠재적 일시 중단 지점)로 지정된다.async로 선언한 함수가 완료될 때까지 일시 중지 되는 지점이다.


예를 들어, 네트워크로 요청한 데이터를 다 가져올 때까지 작업을 일시 중단 해야겠으니 async로 데이터 요청을 하고 await 지점에서 대기하는 것이다.


위 설명에선 potential suspedsion points를 매우 간단하게 설명하였다. 좀 더 자세히 알아보자



Async, Await를 처리하는 내부 원리 (스레드 관리)


sync(동기)에서의 스레드 관리 

 호출 - A함수에서 B함수(sync)를 호출하면, A함수가 실행되던 스레드의 제어권을 B에게 전달 

 진행 - B함수가 끝날때까지 해당 스레드는 점유되어서 다른 일을 수행하지 않게되는 원리 

 종료 - B함수가 종료되면 A함수에게 다시 스레드 제어권을 반납


아래 예시코드를 살펴보자


위의 예시 코드에서는 functionA에서 functionB를 동기적으로 호출하고 있다. 이때 functionB의 실행이 끝날 때까지 functionA가 실행 중인 스레드가 점유되어 다른 작업을 수행하지 않는다. 따라서 "Function A 시작"과 "Function A 종료" 사이에 "Function B 시작"과 "Function B 종료"가 출력된다.




async에서의 스레드 관리 

호출 - A함수에서 B함수(async)를 호출하면, A함수가 실행되던 스레드의 제어권을 B에게 전달 

진행 - B함수는 async이기 때문에 스레드의 제어권을 포기하는 suspend가 가능 (suspend되면 호출한 A함수도 같이 suspend됨) 

suspend - 스레드에 대한 제어권은 system으로 가고 시스템은 스레드를 사용하여 다른 작업을 수행      resume - 일시 중단된 비동기 함수 B를 다시 실행하는 단계 

 종료 - B함수가 종료되면 A함수에게 스레드 제어권을 반납      


아래 코드도 한번 살펴보자

코드의 실행 순서는 다음과 같다.

메인 스레드에서 Task가 시작되면서 "메인 스레드 시작"이 출력됩니다.

Task 내부에서 functionA가 호출되면서 "Function A 시작"이 출력됩니다.

functionA 내부에서 Task를 사용하여 functionB를 비동기적으로 호출합니다. 이 때 "Function B 호출"이 출력됩니다.

functionB가 실행되면서 "Function B 시작"이 출력됩니다.

functionB의 비동기 작업인 1초간 대기가 수행됩니다.

대기가 완료되면 "Function B 종료"가 출력됩니다.

functionB의 실행이 완료되면 "Function B 종료 후 다시 Function A로 돌아옴"이 출력됩니다.

functionA의 실행이 완료됩니다.

메인 스레드에서 "메인 스레드에서 다른 작업 수행"이 출력됩니다.

Task.yield를 통해 스레드 제어권을 시스템에 양도합니다.

다른 작업이 수행되는 동안 "메인 스레드 종료"가 출력됩니다.


이렇게 코드를 작성하면 비동기 작업을 수행하면서 스레드 제어권을 적절하게 전달하고, Task.yield 를 사용하여 다른 작업을 수행할 수 있다. 


참조코드

위의 코드는 3초동안 대기하다 "sleep 끝" 문자열을 반환하는 함수이다

위 함수를 동기적으로 처리하면 3초동안 프로그램이 멈추는 아주 끔찍한 사태가 발생하기 때문에

반드시 비동기적으로 처리해야 한다. 함수명인 asyncTest 옆에 async를 붙여주고

Task.sleep에서 에러를 낼 가능성이 있으므로 throws도 붙여준다

async 메서드를 호출하려면 async 메서드 내에서 호출되거나Task로 묶어서 호출해야 한다

위 코드를 실행시키면 3초 후 sleep 끝 메시지가 출력될 것이다.



참조 자료

https://jeong9216.tistory.com/498

https://ios-development.tistory.com/958

매거진의 이전글 Swift의 @escaping 클로저
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari