iOS/Common

iOS에서의 GCD와 동기, 그리고 비동기(with ChatGPT)

TDCIAN 2023. 4. 10. 14:06

미쳤습니까 휴먼?

 

 

Q. iOS에서 GCD와 동기, 비동기 프로그래밍에 대한 정의를 알려줘

 

A. iOS 개발에서 Grand Central Dispatch (GCD)는 멀티스레딩 환경에서 비동기적인 작업을 실행하는 데 사용되는 Apple의 기술입니다. GCD를 사용하면 애플리케이션의 성능을 향상하고 사용자 경험을 향상하는 데 도움이 됩니다.

 

GCD는 두 가지 주요 개념인 동기(synchrounous)와 비동기(asynchrounous) 프로그래밍을 사용합니다.

 

동기(synchrounous) 프로그래밍은 작업이 순차적으로 실행되며 한 작업이 끝나기 전에 다른 작업이 실행되지 않습니다.

반면, 비동기(asynchronous) 프로그래밍은 작업이 동시에 실행되며 작업이 완료되지 않았더라도 다른 작업이 실행됩니다.

비동기 프로그래밍을 사용하면 애플리케이션의 반응성을 향상시키고 블로킹(blocking) 작업이 UI 스레드를 차단하는 것을 방지할 수 있습니다.

 

GCD는 다음과 같은 비동기 메서드를 제공합니다.

1. dispatch_async: 지정된 블록을 비동기적으로 실행합니다.

2. dispatch_barrier_async: 지정된 블록을 실행하기 전에 대기열의 다른 모든 블록이 완료될 때까지 대기합니다. 이를 사용하면 동시에 실행되는 다른 작업이 해당 블록과 충돌하지 않도록 보장할 수 있습니다.

3. dispatch_group_async: 지정된 작업을 디스패치 그룹에 추가하고 해당 그룹의 모든 작업이 완료될 때까지 기다립니다.

 

위의 메서드들은 모두 비동기적으로 작동하며, GCD는 백그라운드 스레드에서 이러한 작업을 처리합니다.

이를 통해 UI 스레드를 차단하지 않고도 다양한 작업을 비동기적으로 처리할 수 있습니다.

 

 

Q. 위 내용들을 Swift 코드로 설명해 줘

 

A. 코드 예시를 통해 GCD의 동기/비동기 프로그래밍을 설명해드리겠습니다.

// 동기적인 작업
print("Task 1")
print("Task 2")
print("Task 3")

위 코드는 동기적으로 실행되며, "Task 1", "Task 2", "Task 3"의 순서대로 출력됩니다.

 

// 비동기적인 작업
DispatchQueue.global(qos: .userInitiated).async {
	print("Task 1")
    DispatchQueue.main.async {
    	print("Task 2")
    }
    print("Task 3")
}

위 코드는 비동기적으로 실행되며, 백그라운드 스레드에서 "Task 1"과 "Task 3"이 출력된 후, 메인 스레드에서 "Task 2"가 출력됩니다. 이를 통해, "Task 2"가 출력되기 전까지 다른 작업을 실행할 수 있다는 것을 알 수 있습니다.

 

디스패치 그룹을 사용한 비동기 작업

let group = DispatchGroup()

group.enter()
DispatchQueue.global(qos: .userInitiated).async {
	print("Task 1")
    group.leave()
}

group.enter()
DispatchQueue.global(qos: .userInitiated).async {
	print("Task 2")
    group.leave()
}

group.notify(queue: DispatchQueue.main) {
	print("All tasks are completed.")
}

위 코드는 dispatch_group_async 메서드를 사용하여 비동기 작업을 디스패치 그룹에 추가하고 해당 그룹의 모든 작업이 완료될 때까지 기다립니다. "Task 1"과 "Task 2"가 각각 별도의 백그라운드 스레드에서 실행되며, 모든 작업이 완료된 후 "All tasks are completed."가 출력됩니다.

 

 

 

Q. 위 코드에서 qos는 무엇을 의미하는지, 그리고 어떤 종류들이 있는지 알려줘

 

A. 'qos'는 Quality of Service(서비스 품질)의 약자로, GCD에서 스레드의 우선순위를 지정하는 데 사용됩니다. 'qos'를 사용하여 다양한 우선순위의 스레드를 생성하고 작업을 할당할 수 있습니다.

 

GCD에서는 다음과 같이 5가지의 서비스 품질(QoS) 레벨을 제공합니다.

 

(1) .userInteractive

유저 인터페이스와 관련된 작업, 즉 즉각적인 결과가 필요한 작업에 사용됩니다.

UI 업데이트, 애니메이션 처리, 사용자의 이벤트 처리 등에 적합합니다.

// .userInteractive: UI 작업에 적합한 QoS 레벨
DispatchQueue.global(qos: .userInteractive).async {
	// UI 업데이트 작업 등
}

 

(2) .userInitiated

유저가 시작한 작업으로, 즉각적인 결과가 필요하지 않은 작업에 사용됩니다.

파일 다운로드, 인쇄 작업 등이 해당됩니다.

// .userInitiated: 유저가 시작한 작업에 적합한 QoS 레벨
DispatchQueue.global(qos: .userInitiated).async {
	// 파일 다운로드, 인쇄 작업 등
}

 

(3) .utility

긴 시간이 소요되는 작업에 사용됩니다.

사용자가 기다리는 것이 필요하지 않은 작업에 사용됩니다.

// .utility: 긴 시간이 소요되는 작업에 적합한 QoS 레벨
DispatchQueue.global(qos: .utility).asnyc {
	// 큰 데이터 파일 처리, 긴 계산 작업 등
}

 

(4) .background

백그라운드 작업으로, 사용자가 직접적으로 인지하지 않는 작업에 사용됩니다.

파일 백업, 자동 업데이트 등이 해당됩니다.

// .background: 백그라운드 작업에 적합한 QoS 레벨
DispatchQueue.global(qos: .background).asnyc {
	// 자동 업데이트, 파일 백업 등
}

 

(5) .default

다른 QoS를 명시하지 않은 경우 기본적으로 사용되는 QoS로, .utility와 비슷한 레벨입니다.

// .default: 기본적으로 사용되는 QoS 레벨
DispatchQueue.global(qos: .default).asnyc {
	// 일반적인 작업 등
}

 

각각의 QoS 레벨은 시스템이 자동으로 해당 우선순위에 맞는 적절한 스레드를 할당해 줍니다.

따라서 개발자는 우선순위를 직접 지정할 필요는 없지만, 작업에 맞는 적절한 QoS를 선택하여

성능을 향상시킬 수 있습니다.

 

 

 

Q. GCD를 사용할 때 반드시 sync를 사용해야 하는 경우가 있다면 알려줘

 

A. GCD에서 sync 메서드는 작업이 완료될 때까지 현재 스레드를 블록 하며 대기하는 동기 방식으로 작업을 실행합니다.

따라서 sync 메서드를 사용하면 현재 스레드가 블록 되기 때문에, 다른 작업을 수행할 수 없게 됩니다.

그렇기 때문에 sync 메서드는 주로 특정 스레드가 작업을 완료한 후에 다른 작업을 수행해야 하는 경우에 사용됩니다.

 

그러나 반드시 sync 메서드를 사용해야 하는 경우는 없습니다.

sync 메서드는 대부분의 경우를 제외하고는 async 메서드를 사용하여 비동기적으로 작업을 실행하는 것이 좋습니다.

비동기 방식으로 작업을 실행할 경우, 작업이 완료될 때까지 대기하지 않고 다른 작업을 수행할 수 있기 때문에

앱의 반응성을 유지하면서 성능을 개선할 수 있습니다.

 

따라서, GCD를 활용할 때에는 가능한 한 async 메서드를 사용하여 비동기적으로 작업을 실행하는 것이 좋습니다.

sync 메서드를 사용해야 하는 경우가 있다면, 해당 작업을 수행하는 스레드가 현재 스레드와 다른 스레드인지 확인하고, 현재 스레드를 블록 하지 않도록 주의해야 합니다.

 

 

 

Q. sync를 활용하는 코드를 알려줘

 

A. 아래는 GCD에서 sync 메서드를 사용하는 간단한 예제 코드입니다.

이 코드는 두 개의 큐에서 각각의 작업을 실행한 후, 두 작업이 완료되면 그 결과를 합쳐서 출력하는 예제입니다.

let queue1 = DispatchQueue(label: "com.example.queue1")
let queue2 = DispatchQueue(label: "com.example.queue2")

queue1.sync {
	// 첫 번째 큐에서 실행할 작업
    for i in 0..<10 {
    	print("queue1: \(i)")
    }    
}

queue2.sync {
	// 두 번째 큐에서 실행할 작업
    for i in 0..<10 {
    	print("queue2: \(i)")
    }    
}

// 두 작업이 모두 완료된 후 결과 출력
print("Finished!")

위 코드에서 queue1과 queue2는 각각 별도의 큐로, sync 메서드를 사용하여 각 큐에서 작업을 실행합니다.

이 코드에서는 sync 메서드를 사용하고 있지만, async 메서드를 사용하여 비동기적으로 작업을 실행하는 것이 좋을 수도 있습니다.

async 메서드를 사용하여 작업을 실행할 경우, 작업이 완료되는 순서와 결과가 예측하기 어려울 수 있지만, 앱의 반응성을 유지할 수 있다는 장점이 있습니다.

 

 

Q. iOS에서 blocking과 non blocking, sync와 async의 관계에 대해 설명해 줘

 

A. iOS에서 blocking과 non-blocking, sync와 async는 서로 밀접한 관련이 있습니다.

(1) blocking vs non-blocking

- blocking은 호출된 함수가 작업을 완료하기 전까지 제어권을 반환하지 않는 것을 의미합니다.

다시 말해, 함수가 작업을 완료하기 전까지 호출된 코드가 멈추게 됩니다.

- non-blocking은 호출된 함수가 작업을 완료하기 전에도 제어권을 반환하여,

호출된 코드가 멈추지 않고 다른 작업을 수행할 수 있도록 합니다.

 

(2) sync vs async

- sync는 작업이 완료될 때까지 호출된 코드가 대기하는 동기 방식을 의미합니다.

즉, 작업이 완료되기 전까지는 다른 작업을 수행할 수 없습니다.

- async는 작업이 완료될 때까지 호출된 코드가 대기하지 않고,

다른 작업을 수행할 수 있도록 하는 비동기 방식을 의미합니다.

 

위의 개념을 조합하면 다음과 같은 경우가 있습니다.

- blocking + sync: 작업이 완료될 때까지 대기하면서, 제어권을 반환하지 않습니다.

- blocking + async: 작업이 완료될 때까지 대기하면서, 다른 작업을 수행할 수 없습니다.

- non-blocking + sync: 작업을 실행하면서, 제어권을 즉시 반환하지 않고 작업이 완료될 때까지 대기합니다.

- non-blocking + async: 작업을 실행하면서, 제어권을 즉시 반환하고 작업이 완료되는 시점에 콜백 함수를 호출하여 결과를 처리합니다.

 

일반적으로 iOS에서는 비동기 방식을 사용하여 작업을 실행하고, 작업이 완료되는 시점에 콜백 함수를 호출하여 결과를 처리합니다.

이를 통해 앱의 반응성을 유지하면서, 다른 작업을 수행할 수 있도록 합니다.

만약 blocking 작업이 필요한 경우, 가능한 한 비동기 방식으로 작업을 실행하고,

blocking 작업이 반드시 필요한 경우에만 sync 메서드를 사용하여 실행하는 것이 좋습니다.

 

 

출처: ChatGPT(https://chat.openai.com/chat)