- Swift Concurrency - Async, Await2023년 08월 08일
- 2료일
- 작성자
- 2023.08.08.:58
기존 GCD’s queue-based model&completion Handler의 문제점?
- Thread Explosion : 내가 쓰레들들 만들수 있어서 수많은 쓰레드속에서 switching이 필요하다. 우리가 왜 쓰레드를 만들었지? 프로세스 context swiching 때문에. 그런데 쓰레드가 너무많이생기면 문제CPU는 이전 쓰레드에서 새 스레드로 전환하려면 전체 쓰레드 컨텍스트 스위치 해야함.
- memory overhead: 블락된 쓰레드는 재실행을 기다리는 동안 메모리, 리소스를 가지고 있다,
- Priority inversion: Qos는 특정 queue에 높은 우선순위 일을 끝내기 위해 낮은 우선순위의 시스템리소스를 뺏었다.
- 비동기 코드가 실행되면 코드가 control을 포기하기전까지 CPU core를 정상적 회수할수없다. >> thread blocking 현상 발생.
- 비동기로 작업을 수행하고 completion closure를 사용하여 해당작업이 끝날때 처리를 해주는데 completion을 사용하는 것을 잊을 경우 문제 발생위험도가 있다.(에러 처리를 위해 모든 case에서 completion handler 리턴했는가?) 컴파일러는 이것을 체크 할수 없어 빌드와 런이 제대로 되지만 실제 버그가 남아있는 경우가 있을수있다.
- reatain cycle발생 가능성. 코드 내부에서 self 키워드 접근해야 한다면 참조 사이클 발생할 수 있다.
- 콜백지옥으로 인한 가독성 문제..
func fetchThumbnail(for id: String, completion: @escaping (Result<UIImage, Error>) -> Void) { let request = thumbnailURLRequest(for: id) let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { completion (.failure (error)) } else if (response as? HTTPURLResponse) ? .statusCode != 200 { completion(.failure(FetchError.badID)) } else { guard let image = UIImage (data: data!) else { completion(.failure(FetchError.badImage)) return } image.prepareThumbnai1(of: CGSize (width: 40, height: 40)) { thumbnail in guard let thumbnail = thumbnail else { completion(.failure(FetchError.badImage)) return } completion (.success (thumbnail)) } } } task.resume()}
func fetchThumbnail(for id: String) async throws -> UIImage let request = thumbnailURLRequest(for: id) let (data, response) = try await URLSession.shared.data(for: request) guard (response as? HTTPURLesponse)?.statusCode == 200 else { throw FetchError. badID } let maybeImage = UIImage (data: data) guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError. badImage } return thumbnail }
Swift 5.5 Councurrency Model
- Cooperative thread pool
- Core수를 thread수가 초과하지 않도록 관리. 즉 runtime때마다 쓰레드 생성, 삭제할 필요없이 이미 thread pool을 만들어놓고 관리 >> code가 pool안에 있는 특정쓰레드에서 빠르게 suspended, resume.
- Compile, runtime에서 코드가 suspend, resume을 알 수 있다.
- 가독성 향상
- race condition처리
- 구조적 동시성! 비동기 작업 간의 종속성 명시적 관리. 작업 실행 스케쥴링.(CPU,thread직접 다루지 않는다. → 여러가지 비동기 작업들을 concurrent하게
let nameList = ["서노","수낵","민킴","주용","진창","니지","7의멤버","8의멤버","9의멤버"] func beforeasync() -> [String] { let group = DispatchGroup() let queue = DispatchQueue(label: "GCD", attributes: .concurrent) var names = [String]() for i in 0..<9 { queue.async(group: group) { Thread.sleep(forTimeInterval: 0.1) let name = nameList[i] names.append(name) } } group.wait() return name }
GCD로 하면 과연 9개의 이름을 다 가져올까? Nono 여러쓰레드가 names에 접근하면서 race condition일어날 위험이 있다.
→ lock을 걸어주어야함
근데 async await는 애초에 오류를 알려줌!
그러면 무조건 async await쓰고 이전방식쓰지마? nono상호배타적이 아니다!
- async : 이 함수가 비동기라는 것을 알려줌
- await: async keyword가 표시된 메소드나 함수의 리턴을 기다림!!! await을 통해 비동기적으로 만든 함수의 결과를 대기 할수 있다.
- throws: 비동기 수행중 문제가 발생하면 바로 에러를 리턴하도록 -> completion handler로 에러발생할수있는곳에 일일이 써야하는 노-력 사라짐!
즉 우리는 어떠한 함수를 비동기 처리 하고 싶을때 붙인다. 그렇다면 이 함수는 어디 쓰레드에서 처리될까?
그건 메인일수도 있고 다른 쓰레드 일수도 있다.우리가 명시적으로 쓰레드를 지정해주지 않아도 알아서
import SwiftUI import Combine class AsyncAwaitBootCampViewModel: ObservableObject { @Published var dateArray: [String] = [] func addAuthor1() async { let author1 = "Author1 : \(Thread.current)" self.dateArray.append(author1) try? await Task.sleep(nanoseconds: 2_000_000_000) let author2 = "Author2 : \(Thread.current)" self.dateArray.append(author2) try? await doSomething() await MainActor.run(body: { let author4 = "Author3: \(Thread.current)" self.dateArray.append(author4) }) } func doSomething() async throws { let author3 = "something1 : \(Thread.current)" self.dateArray.append(author3) } } struct AsyncAwait : View{ @StateObject private var vm = AsyncAwaitBootCampViewModel() var body: some View { List{ ForEach(vm.dateArray, id: \.self) { data in Text(data) } } .onAppear { Task { await vm.addAuthor1() } } } } struct AsyncAwait_Previews: PreviewProvider { static var previews: some View { AsyncAwait() } }
그런데 task. sleep후에는 현재는? 에서는 백그라운드 쓰레드로 바뀌게 된다.
async를 호출하게 된다면 기존 실행컨텍스트 처리하던 쓰레드는 자유로워짐. 작업권을 다른데 넘길수 있다는 뜻!
그런데 doSomething async를 호출했을때는 왜 같은 쓰레드가 찍힐까?
여기는 컨텍스트 스위칭이 있기에 현재스레드를 먼저 우선으로 쓰고 알아서 쓰레드 관리작업을 하는 만큼 다양한 쓰레드로 가서 작업을 해준다.
시스템이 스레드를 알아서 분배 처리하기에 async 호출전 스레드와 await이후의 스레드는 다를 수 있다를 기억!!
- 그러므로 스레드에 관계된 데이터를 유지해서는 안됩니다.
만약 여기서 메인쓰레드에 무조건 해야하는 경우는 mainActor사용
- await을 만나면 현재 쓰레드 제어권을 시스템에게 넘겨줌(현재 쓰레드를 block하는게 아님!!) > 2. 시스템은 해당쓰레드에서 우선순위가 높은 작업을 먼저함. > 3. 작업이 끝나면 다시 스레드 제어권을 줌
프로토콜에서도 async메소드 생성가능
이 async 메서드를 호출하려면 async 메서드 내에서 호출되거나 Task로 묶어서 호출.
'면접준비' 카테고리의 다른 글
Combine(2)- Operator (1) 2023.10.08 TaskGroup (0) 2023.08.13 Combine3-Cancellable (0) 2023.07.09 Combine(1)-WhatisCombine(publish&subscirbe) (0) 2023.07.09 lazy var (1) 2023.07.07 다음글이전글이전 글이 없습니다.댓글
스킨 업데이트 안내
현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)