iOS 비동기 DispatchQueue, DispatchGroup

ChangJin·2023년 7월 26일
0

iOS

목록 보기
1/6
post-thumbnail

다음의 PR을 참고하면 좋아요.
https://github.com/DeveloperAcademy-POSTECH/MC3-Team9-BALANCEIAGA/pull/77

비동기 해보기.
다음 두 개를 사용해 볼거에요.

  • DispatchQueue.global()
  • DispatchGroup()

먼저 두 친구가 무엇인지 알아야 해요.


DispatchQueue

공식문서 https://developer.apple.com/documentation/dispatch/dispatchqueue

  • 앱의 메인 스레드나 혹은 백그라운드 스레드에서 concurrently한 태스크의 실행을 관리해요.
  • 디스패치 큐는 작업을 순차적으로 또는 동시에 실행해요.
  • 작업 항목을 동기식 또는 비동기식으로 예약해요.

기본 대기열에서 작업 항목을 동기식으로 실행하려고 하면 데드락이 발생해요

  • 어떻게 해결해야 할까요?

    예시를 보면서 생각해볼게요

    import Foundation
      
    let queue1 = DispatchQueue(label: "example.queue1")
    let queue2 = DispatchQueue(label: "example.queue2")
    
    func task1() {
        queue2.sync {
            print("Task 1: Waiting for queue2")
            queue1.sync {
                print("Task 1: Executing on queue1")
            }
        }
    }
    
    func task2() {
        queue1.sync {
            print("Task 2: Waiting for queue1")
            queue2.sync {
                print("Task 2: Executing on queue2")
            }
        }
    }
    
    queue1.async {
        task1()
    }
    
    queue2.async {
        task2()
    }
  • 두 개의 큐 queue1과 queue2를 생성하고, task1, task2 함수가 각각 다른 큐에 실행되도록 만들었어요.

  • queue1과 queue2에 각각의 작업을 비동기로 추가하여 실행하고 있어요.

  • 이 코드에서 task1은 queue2에서 동기적으로 queue1로 접근하려고 하고, task2는 queue1에서 동기적으로 queue2로 접근하려고 합니다. 이렇게 서로 다른 큐에서 블록된 상태에서 서로의 큐를 대기하게 되면 데드락이 발생할 수 있습니다.


데드락을 해결하자

  1. serialQueue 사용하기: 데드락의 주요 원인은 서로 다른 큐에서 상호 대기하는 것이에요. 이를 해결하기 위해 하나의 serialQueue를 사용하여 작업들을 순차적으로 실행하면 데드락을 피할 수 있어요.
let serialQueue = DispatchQueue(label: "com.example.serialQueue")

func task1() {
    serialQueue.sync {
        print("Task 1: Executing on serialQueue")
    }
}

func task2() {
    serialQueue.sync {
        print("Task 2: Executing on serialQueue")
    }
}

serialQueue.async {
    task1()
}

serialQueue.async {
    task2()
}
  1. 큐 조합 순서 변경하기 : 데드락을 발생시키는 원인 중 하나는 큐에 접근하는 순서에요. 하나의 큐에 대한 작업이 먼저 실행되도록 순서를 변경하여 데드락을 피할 수 있어요.
queue1.async {
    task1()
    queue2.async {
        task2()
    }
}


DispatchGroup

공식문서 https://developer.apple.com/documentation/dispatch/dispatchgroup

  • 하나의 유닛으로서 우리가 관리하는 태스크들의 그룹이에요.

  • 여러 작업 항목을 그룹에 연결하고 동일한 대기열 또는 다른 대기열에서 비동기식으로 실행하도록 예약할 수 있어요.

  • 모든 작업이 완료되면 DispatchGroup은 핸들러를 통해 완료되었다는 것을 알릴 수 있어요.

    예시를 보면서 생각해볼게요

import SwiftUI

struct ContentView: View {
    @State private var isLoading = true

    var body: some View {
        VStack {
            if isLoading {
                Text("Loading...")
            } else {
                Text("Data Loaded!")
            }
        }
        .onAppear {
            let dispatchGroup = DispatchGroup()

            // 비동기 작업 1
            dispatchGroup.enter()
            DispatchQueue.main.async {
                self.fetchDataFromAPI1 {
                    dispatchGroup.leave()
                }
            }

            // 비동기 작업 2
            dispatchGroup.enter()
            DispatchQueue.main.async {
                self.fetchDataFromAPI2 {
                    dispatchGroup.leave()
                }
            }

            // DispatchGroup의 모든 작업이 완료될 때까지 기다려요
            dispatchGroup.notify(queue: .main) {
                // 여기에 DispatchGroup의 모든 작업이 완료되었을 때 실행할 로직 추가해요
                self.isLoading = false
                self.anotherFunction()
            }
        }
    }

    // 예시를 위한 가상의 비동기 API 요청 함수 1
    func fetchDataFromAPI1(completion: @escaping () -> Void) {
        DispatchQueue.global().asyncAfter(deadline: .now() + 3) {
            // 비동기 작업 시뮬레이션을 위해 3초 대기해요
            completion()
        }
    }

    // 예시를 위한 가상의 비동기 API 요청 함수 2
    func fetchDataFromAPI2(completion: @escaping () -> Void) {
        DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
            // 비동기 작업 시뮬레이션을 위해 5초 대기해요
            completion()
        }
    }

    // 기타 다른 함수
    func anotherFunction() {
        // DispatchGroup의 모든 작업이 완료된 후에 실행되는 함수에요.
        print("All async tasks completed. Running anotherFunction()")
        // 이곳에 원하는 로직을 추가해요.
    }
}
  • DispatchGroup()으로 다른 비동기 요청 처리가 다 되었을 때 핸들러를 실행시켜요.
  • 핸들러를 통해 모든 비동기 처리가 완료되었다는 것을 알 수 있어요.
  • API 통신을 할때 요긴하게 쓰이니 잘 알아두어야 해요.
profile
게임 프로그래머

2개의 댓글

comment-user-thumbnail
2023년 7월 26일

좋은 글 감사합니다. 자주 올게요 :)

1개의 답글