카테고리 없음

swift DispachQueue 대해 알아보자

kingarthur 2024. 8. 8. 11:50

DispatchQueue는 GCD(Grand Central Dispatch)의 일환으로 애플의 iOS 및 macOS에서 멀티스레딩 및 동시성을 관리하는 도구입니다. DispatchQueue는 코드 블록을 큐에 보내고, 시스템이 해당 블록을 비동기적으로 또는 동기적으로 실행할 수 있게 합니다.

 

그럼 큐랑 동시성 멀티스레딩 ??? 그게 먼지 보자 

 

큐(Queue)

큐는 작업을 차례로 처리하기 위해 사용되는 자료 구조입니다. 작업이 큐에 추가되면, 큐는 이를 차례로 꺼내어 실행합니다. Swift에서는 DispatchQueue와 OperationQueue가 이에 해당합니다.

  • DispatchQueue: GCD(Grand Central Dispatch)의 일부로, 작업을 관리하는 간단한 큐입니다.
  • OperationQueue: 더 정교한 동시성 제어를 제공하는 큐로, 작업 간의 의존성 및 동시 실행 제한 등을 설정할 수 있습니다.

멀티 스레딩(Multi-threading)

멀티 스레딩은 애플리케이션에서 여러 스레드를 사용하여 동시에 여러 작업을 실행하는 방법입니다. 이는 시스템 자원을 더 효율적으로 사용하고, 애플리케이션의 성능을 향상시킬 수 있습니다.

  • 스레드(Thread): 프로세스 내에서 실행되는 가장 작은 실행 단위입니다.
  • 메인 스레드(Main Thread): UI 업데이트와 같은 주요 작업이 실행되는 기본 스레드입니다.
  • 백그라운드 스레드(Background Thread): 시간이 오래 걸리는 작업을 실행하여 메인 스레드의 작업을 방해하지 않도록 하는 스레드입니다.

멀티 스레딩의 예시

DispatchQueue.global().async {
    // 이 블록은 백그라운드 스레드에서 실행됩니다.
    // 시간이 오래 걸리는 작업을 여기서 처리합니다.
    let result = longRunningTask()
    
    DispatchQueue.main.async {
        // 이 블록은 메인 스레드에서 실행됩니다.
        // UI 업데이트 작업을 여기서 처리합니다.
        updateUI(with: result)
    }
}

동시성(Concurrency)

동시성은 여러 작업이 동시에 실행되는 것을 의미합니다. 이는 멀티 스레딩을 통해 이루어질 수 있지만, 비동기 작업을 통해서도 달성할 수 있습니다. 동시성은 여러 작업이 동시에 진행되어야 할 때 중요한 개념입니다.

  • 동시 실행(Concurrent Execution): 여러 작업이 동시에 실행되는 것.
  • 비동기 작업(Asynchronous Task): 작업이 다른 작업과 독립적으로 실행되어, 하나의 작업이 완료될 때까지 다른 작업을 막지 않는 것.

동시성의 예시

비동기 작업을 사용하여 동시성을 구현할 수 있습니다.

DispatchQueue.global().async {
    // 첫 번째 작업
    let result1 = longRunningTask1()
    
    DispatchQueue.global().async {
        // 두 번째 작업
        let result2 = longRunningTask2()
        
        DispatchQueue.main.async {
            // 두 작업의 결과를 사용하여 UI 업데이트
            updateUI(with: result1, and: result2)
        }
    }
}

동기(Synchronous) vs. 비동기(Asynchronous)

  • 동기(Synchronous): 작업이 완료될 때까지 현재 스레드가 기다립니다. 다음 작업은 이전 작업이 완료된 후에 실행됩니다.
  • 비동기(Asynchronous): 작업이 시작되면 현재 스레드는 작업이 완료될 때까지 기다리지 않습니다. 다음 작업이 병렬로 실행될 수 있습니다.

동기 작업 예시

let result = syncTask()
// 작업이 완료된 후에 다음 코드가 실행됩니다.
updateUI(with: result)

비동기 작업 예시

asyncTask { result in
    // 작업이 완료되면 이 블록이 실행됩니다.
    updateUI(with: result)
}
// 작업이 완료되기를 기다리지 않고 다음 코드가 실행됩니다.

이러한 개념을 이해하면, 애플리케이션의 성능을 최적화하고 사용자 경험을 개선할 수 있습니다.

DispatchQueue 글로벌 (Global)

DispatchQueue.global()은 글로벌 큐를 반환하며, 이 큐는 시스템에서 제공하는 글로벌 수준의 동시성 큐입니다. 글로벌 큐는 여러 가지 품질의 서비스(QoS) 수준을 제공하며, 시스템이 자원의 우선순위를 정하는 데 도움을 줍니다.

예시

DispatchQueue.global().async {
    // 이 블록은 백그라운드 스레드에서 실행됩니다.
    // 네트워크 요청, 파일 입출력 등 시간이 오래 걸리는 작업을 여기서 처리합니다.
}

DispatchQueue 메인 (Main)

DispatchQueue.main은 메인 큐를 반환하며, 메인 큐는 애플리케이션의 메인 스레드에서 실행됩니다. UI 업데이트 및 기타 주요 작업은 메인 스레드에서 이루어져야 하므로, 메인 큐는 이를 관리합니다.

예시

DispatchQueue.main.async {
    // 이 블록은 메인 스레드에서 실행됩니다.
    // UI 업데이트 등 메인 스레드에서 처리해야 하는 작업을 여기서 처리합니다.
}

 

조합하여 사용하기

주로 긴 작업(예: 네트워크 호출, 파일 읽기 등)을 백그라운드 스레드에서 수행하고, 완료된 후 결과를 메인 스레드에서 처리해야 할 때 글로벌 큐와 메인 큐를 조합하여 사용합니다. 예를 들어 이미지 다운로드 후 UI 업데이트를 하는 경우가 대표적입니다.

전체 예시 코드

private func downloadImage(from url: URL, completion: @escaping (UIImage) -> Void) {
    // 글로벌 큐에서 백그라운드 작업 실행
    DispatchQueue.global().async {
        // URL에서 데이터를 다운로드
        if let imageData = try? Data(contentsOf: url),
           // 데이터를 UIImage로 변환
           let image = UIImage(data: imageData) {
            // 메인 큐에서 UI 업데이트
            DispatchQueue.main.async {
                completion(image)
            }
        }
    }
}

 

이 코드는 URL에서 이미지를 다운로드하고, 다운로드가 완료되면 메인 스레드에서 UI를 업데이트하는 패턴을 보여줍니다.

 

그럼 .async 비동기 처리 했다면 반대로 동기 처리는? 

 

동기처리 이유에 대해 알아보자 

 

비동기 처리를 사용하지 않고 긴 작업을 수행하는 방법도 있지만, 이는 애플리케이션의 성능과 응답성을 저하시킬 수 있습니다. 그럼에도 불구하고, 동기적으로 작업을 처리하는 방법과 그 예시를 설명드리겠습니다.

동기 처리

동기 처리는 작업이 완료될 때까지 현재 스레드가 멈추고 기다리는 방식입니다. 이는 코드가 순차적으로 실행되며, 작업이 완료될 때까지 다음 코드를 실행하지 않습니다.

동기 처리 예시

아래 코드는 URL에서 이미지를 동기적으로 다운로드하는 예시입니다.

private func downloadImageSynchronously(from url: URL) -> UIImage? {
    // URL에서 데이터를 다운로드
    if let imageData = try? Data(contentsOf: url),
       // 데이터를 UIImage로 변환
       let image = UIImage(data: imageData) {
        return image
    }
    return nil
}

 

이 코드는 다음과 같이 사용될 수 있습니다:

let url = URL(string: "https://example.com/image.png")!
if let image = downloadImageSynchronously(from: url) {
    // UI 업데이트
    imageView.image = image
}

동기 처리의 문제점

  1. UI 멈춤: 동기 작업이 메인 스레드에서 실행되면, 작업이 완료될 때까지 UI가 멈추게 됩니다. 이는 사용자 경험을 저하시킬 수 있습니다.
  2. 앱 응답성 저하: 네트워크 요청이나 파일 입출력과 같은 시간이 오래 걸리는 작업을 동기적으로 처리하면, 앱 전체가 느려지거나 응답하지 않게 될 수 있습니다.

동기 처리의 적절한 사용

동기 처리가 적절한 경우도 있습니다. 예를 들어, 초기 설정이나 앱 시작 시 짧은 초기화 작업을 동기적으로 처리할 수 있습니다. 그러나 네트워크 요청이나 대용량 데이터 처리와 같은 작업은 비동기적으로 처리하는 것이 좋습니다.

대안 방법: OperationQueue 사용

동기 및 비동기 작업을 조합할 수 있는 또 다른 방법으로 OperationQueue를 사용할 수 있습니다. 이는 더 정교한 동시성 제어를 제공합니다.

OperationQueue 예시

let queue = OperationQueue()

queue.addOperation {
    // 백그라운드에서 작업 실행
    if let url = URL(string: "https://example.com/image.png"),
       let imageData = try? Data(contentsOf: url),
       let image = UIImage(data: imageData) {
        OperationQueue.main.addOperation {
            // 메인 큐에서 UI 업데이트
            imageView.image = image
        }
    }
}

 

이 예시는 백그라운드 큐에서 이미지를 다운로드하고, 완료되면 메인 큐에서 UI를 업데이트합니다. OperationQueue를 사용하면 동시 실행 작업 수를 제한하거나 작업 간의 의존성을 설정할 수 있는 장점이 있습니다.

 

그래서 잘 이해하고 쓰면 좋은 어플리케이션을 만들수 있습니다.