본문 바로가기

정보

DispatchQueue.main.async와 DispatchQueue.main.sync의 차이점은 무엇인가요?

 

1. .async와 .sync의 차이는 무엇인가?

1.1 DispatchQueue.main.async:

  • 비동기적으로 작업을 메인 스레드에 추가합니다.
  • 현재 실행 중인 코드가 즉시 반환되고, 메인 스레드의 RunLoop에 따라 작업이 실행됩니다.
  • Deadlock(교착상태)이 발생하지 않으며, UI 작업에 안전하게 사용할 수 있습니다.

 

1.2 DispatchQueue.main.sync:

  • 동기적으로 작업을 메인 스레드에 추가합니다.
  • 호출한 스레드는 작업이 완료될 때까지 대기합니다.
  • 만약 메인 스레드에서 다시 DispatchQueue.main.sync를 호출하면 Deadlock(교착 상태)이 발생합니다.

🟥  .async와 .sync의 차이: 대기 방식

  1. .async는 "작업을 추가하고 바로 반환"
    • .async는 작업을 대기열에 추가한 뒤 즉시 반환합니다. 즉, 호출한 스레드가 이 작업의 완료 여부와 관계없이 다음 작업을 계속 실행합니다.
    • 이 때문에 "모든 작업이 끝난 뒤 실행"이 언제일지 몰라도, 호출한 스레드(메인 스레드)는 멈추지 않으므로 Deadlock이 발생하지 않습니다.
  2. .sync는 "작업을 추가하고 기다림"
    • .sync는 작업을 대기열에 추가한 뒤, 해당 작업이 완료될 때까지 호출한 스레드가 기다립니다.
    • 이 경우, 메인 큐가 직렬 큐이기 때문에 현재 실행 중인 작업이 끝나지 않으면 대기열의 작업이 실행될 수 없습니다.
    • 하지만 .sync는 작업 완료를 기다리기 때문에 호출한 스레드가 멈추며, Deadlock이 발생합니다.

 

2. 예를 들어서 알아보면

2.1 DispatchQueue.main.async 예제

async는 비동기적으로 작업을 메인 큐에 추가합니다.

이 경우, 현재 실행 중인 코드는 즉시 반환되며, 메인 큐에서 실행될 준비가 되면 작업이 실행됩니다.

func updateUIAsync() {
    DispatchQueue.global().async {
        // 백그라운드 작업 (예: 네트워크 호출)
        let data = "Fetched Data"
        print("Background work completed")
        
        // UI 업데이트는 메인 스레드에서
        DispatchQueue.main.async {
            print("UI updated with data: \(data)")
        }
    }
}

updateUIAsync()

 

아래는 출력된 내용입니다. 

UI 업데이트를 메인 스레드에서 안전하게 수행하는 일반적인 방식입니다.

Background work completed
UI updated with data: Fetched Data

 

 

 

2.2 DispatchQueue.main.sync 예제

sync는 동기적으로 작업을 메인 큐에 추가하며, 호출한 스레드는 해당 작업이 완료될 때까지 기다립니다.

이 경우, 만약 메인 스레드에서 DispatchQueue.main.sync를 호출하면 Deadlock이 발생합니다.

func updateUISync() {
    DispatchQueue.global().async {
        // 백그라운드 작업 (예: 데이터 처리)
        let data = "Processed Data"
        print("Background work completed")
        
        // 동기적으로 UI 업데이트
        DispatchQueue.main.sync {
            print("UI updated with data: \(data)")
        }
    }
}

updateUISync()

 

아래는 출력된 내용입니다. 

위 코드에서는 백그라운드 스레드에서 DispatchQueue.main.sync를 호출하므로, 메인 스레드에서 작업을 안전하게 처리할 수 있습니다.

Background work completed
UI updated with data: Processed Data

 

Deadlock 발생 예제:

아래와 같은 경우, 메인 스레드에서 다시 메인 큐에 동기 작업을 추가하면 Deadlock이 발생합니다.

이유는 메인 스레드가 현재 실행 중인 작업을 완료하기 전에 DispatchQueue.main.sync에서 대기 상태에 빠지기 때문입니다. 이 작업은 절대 완료되지 않으므로 교착 상태가 발생합니다.

func causeDeadlock() {
    // 메인 스레드에서 호출
    DispatchQueue.main.sync {
        print("This will cause a Deadlock!")
    }
}

DispatchQueue.main.async {
    causeDeadlock()
}

 

 

3.실제로 왜 DispatchQueue.main.sync를 잘 사용하지 않는가?

  • 대부분의 UI 업데이트는 비동기적으로 처리(async)하는 것이 안전하고 효율적입니다.
  • 동기 작업(sync)은 호출한 스레드가 대기하기 때문에 잘못 사용하면 Deadlock의 위험이 있습니다.
  • DispatchQueue.main.sync는 꼭 필요한 경우(즉, 작업이 완료된 후에야 다음 코드를 실행해야 하는 경우)에만 신중하게 사용해야 합니다.

 

4. 권장 사용 방식

  • UI 업데이트: 항상 DispatchQueue.main.async를 사용하여 Deadlock 위험을 제거합니다.
  • 동기 작업이 꼭 필요할 경우, 백그라운드 스레드에서만 DispatchQueue.main.sync를 사용합니다.