Dispatch(a.k.a. GCD) 문제 #1
Replies: 4 comments
-
DispatchQueue.global(qos: .userInitiated).async {
print("Task 1")
DispatchQueue.main.async {
print("Task 2")
}
}
print("Task 3")🎤 출력 순서 예측 결과 Task 3
Task 1
Task 25. 다음 코드의 문제점을 찾고, 올바른 수정 방법을 제시하세요. func updateUserInterface() {
DispatchQueue.global().async {
// UI 업데이트 코드
label.text = "Updated"
}
}🎤 문제점: UI 업데이트는 메인 큐에서 이루어져야합니다. func updateUserInterface() {
// 1. global() -> main
DispatchQueue.main.async {
// 2. 클로저 내부 self
self.label.text = "Updated"
}
}6. 아래 코드에서 let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
// 네트워크 작업 시뮬레이션
sleep(2)
print("Network Task 1 completed")
group.leave()
}
group.enter()
DispatchQueue.global().async {
// 이미지 다운로드 시뮬레이션
sleep(3)
print("Image Download completed")
group.leave()
}
group.notify(queue: .main) {
print("All tasks completed")
}🎤group.enter() 호출 횟수와 group.leave() 호출 횟수가 동일해질 때 notify 블록이 실행 7. 다음 코드의 실행 결과와 let workItem = DispatchWorkItem {
for i in 1...5 {
print("Working: \(i)")
sleep(1)
}
}
DispatchQueue.global().async(execute: workItem)
// 2초 후 작업 취소
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
workItem.cancel()
}🎤
Working 1
Working 2
Working 3
Working 4
Working 58. 다음 시나리오를 해결하는 코드를 작성하세요:
힌트: 9. 다음 클로저에서 발생할 수 있는 메모리 순환 참조를 해결하는 코드를 작성하세요. class NetworkManager {
func fetchData(completion: @escaping () -> Void) {
DispatchQueue.global().async {
// 네트워크 작업
self.processData()
completion()
}
}
}class NetworkManager {
func fetchData(completion: @escaping () -> Void) {
DispatchQueue.global().async { [weak self] in
// 네트워크 작업
self?.processData()
completion()
}
}
}10. Race condition의 주요 원인 세 가지를 나열하고, 각각의 문제를 해결할 수 있는 동기화 메커니즘을 제시하세요. 11. 다음 코드에서 발생할 수 있는 문제를 설명하고, 이를 해결하기 위한 방법을 제안하세요: var sharedCounter = 0
let queue = DispatchQueue.global(qos: .userInitiated)
for _ in 1...10 {
queue.async {
sharedCounter += 1
}
}
print("Final Counter: \(sharedCounter)")🎤 global 큐를 비동기로 접근하기 때문에 공유 자원에 동시(concurrent)에 접근하기 때문에 Race Condition 발생 가능성이 있습니다. var sharedCounter = 0
let serialQueue = DispatchQueue(label: "com.example.serialQueue")
for _ in 1...10 {
serialQueue.async {
sharedCounter += 1
}
}
serialQueue.sync {
print("Final Counter: \(sharedCounter)")
}12. 다음 코드에서 Deadlock이 발생하는 이유를 설명하고, 이를 방지하기 위한 방법을 제안하세요: let lock1 = NSLock()
let lock2 = NSLock()
// 1번 큐
DispatchQueue.global().async {
lock1.lock()
sleep(1)
lock2.lock()
print("Task 1 completed")
lock2.unlock()
lock1.unlock()
}
// 2번 큐
DispatchQueue.global().async {
lock2.lock()
sleep(1)
lock1.lock()
print("Task 2 completed")
lock1.unlock()
lock2.unlock()
}🎤 global 큐는 기본적으로 여러 스레드에서 동작하기 때문에 1번 큐에서 13. Deadlock이 발생하기 위해 필요한 네 가지 조건(Mutual Exclusion, Hold and Wait, No Preemption, Circular Wait)을 간단히 설명하세요.
14. Deadlock을 방지하기 위한 일반적인 전략 두 가지를 제시하고, 각각의 장단점을 설명하세요. 15. Priority Inversion이 발생할 수 있는 상황을 설명하세요: let highPriorityQueue = DispatchQueue.global(qos: .userInteractive)
let lowPriorityQueue = DispatchQueue.global(qos: .background)
let semaphore = DispatchSemaphore(value: 1)
lowPriorityQueue.async {
semaphore.wait()
sleep(3)
print("Low priority task completed")
semaphore.signal()
}
highPriorityQueue.async {
semaphore.wait()
print("High priority task completed")
semaphore.signal()
}🎤 우선순위에 따라서 .userInteractive 인 highPriorityQueue가 먼저 실행이 되어야합니다. 16. iOS에서 GCD(Grand Central Dispatch)를 사용할 때 Priority Inversion 문제가 발생하지 않도록 하기 위해 개발자가 따라야 할 권장 사항은 무엇인가요? 17. 다음 코드에서 Dispatch Barrier를 활용하여 위 코드를 수정하고, 수정된 코드가 thread-safe한 이유를 설명하세요. class SharedResource {
private var data: [String] = []
private let queue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
func addItem(_ item: String) {
queue.async {
self.data.append(item)
}
}
func getItems() -> [String] {
return queue.sync {
return self.data
}
}
}
let resource = SharedResource()
DispatchQueue.global().async {
resource.addItem("Item 1")
}
DispatchQueue.global().async {
print(resource.getItems())
}18. 아래 코드는 AccessToken과 RefreshToken을 관리하는 클래스입니다. 이 코드를 thread-safe하게 수정하세요. class TokenManager {
private var accessToken: String = ""
private var refreshToken: String = ""
func updateAccessToken(_ token: String) {
accessToken = token
}
func updateRefreshToken(_ token: String) {
refreshToken = token
}
func getAccessToken() -> String {
return accessToken
}
func getRefreshToken() -> String {
return refreshToken
}
}
let manager = TokenManager()
DispatchQueue.global().async {
manager.updateAccessToken("newAccessToken")
}
DispatchQueue.global().async {
print(manager.getAccessToken())
}🎤 Thread-safe하게 수정해본 코드 class TokenManager {
private var accessToken: String = ""
private var refreshToken: String = ""
private let accessQueue = DispatchQueue(label: "com.token.access")
private let refreshQueue = DispatchQueue(label: "com.token.refresh")
func updateAccessToken(_ token: String) {
accessQueue.sync {
accessToken = token
}
}
func updateRefreshToken(_ token: String) {
refreshQueue.sync {
refreshToken = token
}
}
func getAccessToken() -> String {
accessQueue.sync {
return accessToken
}
}
func getRefreshToken() -> String {
refreshQueue.sync {
return refreshToken
}
}
}
let manager = TokenManager()
DispatchQueue.global().async {
manager.updateAccessToken("newAccessToken")
}
DispatchQueue.global().async {
print(manager.getAccessToken())
} |
Beta Was this translation helpful? Give feedback.
-
func downloadImage(with urlString: String) async throws {
guard let url = URL(string: urlString) else {
throw URLError(.badURL)
}
let (data, _) = try await URLSession.shared.data(from: url)
let image = UIImage(data: data)
}
func fetchData(from urlString: String) async throws -> String {
guard let url = URL(string: urlString) else {
throw URLError(.badURL)
}
let (data, _) = try await URLSession.shared.data(from: url)
let responseString = String(data: data, encoding: .utf8) ?? "No Data"
print("데이터 가져오기 완료: \(urlString)")
return responseString
}
// for문을 통해 처리
DispatchQueue.global(qos: .userInitiated).async {
print("Task 1")
DispatchQueue.main.async {
print("Task 2")
}
}
print("Task 3")
// Task 3
// Task 1
// Task 2
func updateUserInterface() {
DispatchQueue.global().async {
// UI 업데이트 코드
label.text = "Updated"
}
}
// label이 UILabel이라면 메인 스레드에서 접근해야한다.
// global() -> main
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
// 네트워크 작업 시뮬레이션
sleep(2)
print("Network Task 1 completed")
group.leave()
}
group.enter()
DispatchQueue.global().async {
// 이미지 다운로드 시뮬레이션
sleep(3)
print("Image Download completed")
group.leave()
}
group.notify(queue: .main) {
print("All tasks completed")
}
// group.enter()가 두 번 호출 되었기 때문에 각 global 큐 비동기 처리에서 group.leave()가 두 번 모두 호출 될 떄 notify 블록이 실행될 것이다.
let workItem = DispatchWorkItem {
for i in 1...5 {
print("Working: \(i)")
sleep(1)
}
}
DispatchQueue.global().async(execute: workItem)
// 2초 후 작업 취소
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
workItem.cancel()
}
// Working: 1
// Working: 2
// Working: 3
// Working: 4
// Working: 5
// 로 나오며 cancel()메소드가 호출되면 workItem의 isCancelled가 true로 됨 그래서 for문 내에서 workItem.isCancelled를 체크하여서 break 해야함.
let urls = [
"https://github.com/IQAndreas/sample-images/blob/gh-pages/100-100-color/00.jpg?raw=true",
"https://github.com/IQAndreas/sample-images/blob/gh-pages/100-100-color/01.jpg?raw=true",
"https://github.com/IQAndreas/sample-images/blob/gh-pages/100-100-color/02.jpg?raw=true"
]
private let downloadQueue = DispatchQueue(label: "downloadQueue", attributes: .concurrent)
private let semaphore = DispatchSemaphore(value: 3)
private let group = DispatchGroup()
func download() {
for urlString in urls {
group.enter()
semaphore.wait()
downloadQueue.async {
let url = URL(string: urlString)!
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
defer {
semaphore.signal()
group.leave()
}
if let error {
return
}
let image = UIImage(data: data!)
print(image!.size)
}
}
}
}
class NetworkManager {
func fetchData(completion: @escaping () -> Void) {
DispatchQueue.global().async {
// 네트워크 작업
self.processData()
completion()
}
}
}
// async 클로저 내에 [weak self] 혹은 [unowned self]로 레퍼런스 카운트 증가하지 않도록 적용
var sharedCounter = 0 for _ in 1...10 { print("Final Counter: (sharedCounter)")
let highPriorityQueue = DispatchQueue.global(qos: .userInteractive)
let lowPriorityQueue = DispatchQueue.global(qos: .background)
let semaphore = DispatchSemaphore(value: 1)
lowPriorityQueue.async {
semaphore.wait()
sleep(3)
print("Low priority task completed")
semaphore.signal()
}
highPriorityQueue.async {
semaphore.wait()
print("High priority task completed")
semaphore.signal()
}
class SharedResource {
private var data: [String] = []
private let queue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
func addItem(_ item: String) {
queue.async {
self.data.append(item)
}
}
func getItems() -> [String] {
return queue.sync {
return self.data
}
}
}
let resource = SharedResource()
DispatchQueue.global().async {
resource.addItem("Item 1")
}
DispatchQueue.global().async {
print(resource.getItems())
}
// addItem 메소드 내의 queue에서 flag에 .barrier 추가
// 사용하게 되면 thread-safe한 이유
// 배리어를 이용하면 data 배열에 아이템을 추가할 때 작업이 다 끝나야 getItems()에서 data에 접근이 가능해지기 때문에 thread-safe 해진다.
class TokenManager {
private var accessToken: String = ""
private var refreshToken: String = ""
private let queue = DispatchQueue(label: "token", attributes: .concurrent)
func updateAccessToken(_ token: String) {
queue.async(flags: .barrier) {
accessToken = token
}
}
func updateRefreshToken(_ token: String) {
queue.async(flags: .barrier) {
refreshToken = token
}
}
func getAccessToken() -> String {
queue.sync {
return accessToken
}
}
func getRefreshToken() -> String {
queue.sync {
return refreshToken
}
}
}
let manager = TokenManager()
DispatchQueue.global().async() {
manager.updateAccessToken("newAccessToken")
}
DispatchQueue.global().async {
print(manager.getAccessToken())
} |
Beta Was this translation helpful? Give feedback.
-
동시성 프로그래밍이 필요한 이유를 설명하세요. 다음 중 올바른 설명은 무엇인가요?a) CPU의 자원을 최대한 활용하기 위해 b) 사용자의 작업을 순차적으로 처리하기 위해 c) 프로그램의 복잡도를 줄이기 위해 d) 메모리 사용량을 줄이기 위해 📍
2. 동시성과 병렬성(Parallelism)의 차이를 간단히 설명하고, 각각의 장단점을 비교하세요.📍
동기(sync) 순서가 있는 작업을 처리하는데 용이함. 오래걸리는 작업이 있으면 버벅이거나 다른 Task을 할 수 없음 비동기(async) 여러 Task를 동시에 실행 가능. 하지만 Task 들이 끝나는 시점을 보장하지 않아 별도의 처리 필요
직렬 한 쓰레드에서만 Task 처리함. UI 와 관련없는 일련의 작업들을 처리할 수 있지만, 메인쓰레드에서 직렬 방식으로 처리할 경우 해당 쓰레드가 block 되어 아무것도 못함. 병렬 여러 쓰레드에서 동시에 각 Task 들을 처리함. Task 간의 공유 자원을 관리하지 못할경우 여러 문제 발생 (데드락, race condition, 우선순위 역전 등) 3. 다음 상황에서 동시성 프로그래밍이 필요한 이유를 설명하고, 개선 방법을 코드로 작성하세요.📍동시성 프로그래밍이 필요한 이유 용량 이미지 다운로드, API call 과 같은 시간이 오래 걸리는 작업을 수행할 때 앱이
4. 다음 코드의 출력 순서를 예측하고, 그 이유를 설명하세요.DispatchQueue.global(qos:.userInitiated).async{
print("Task 1")
DispatchQueue.main.async{
print("Task 2")
}
}print("Task 1") print("Task 2") print("Task 2") print("Task 1") 둘다 가능. 다른큐에서 실행하므로 순서 보장 x 5. 다음 코드의 문제점을 찾고, 올바른 수정 방법을 제시하세요.func updateUserInterface(){
DispatchQueue.global().async{
// UI 업데이트 코드
label.text="Updated"
}
}UI 업데이트 코드는 무조건 main thread(1번 쓰레드) 에서 수행되어야 한다. 아니면 보라색 워닝이.. 6. 아래 코드에서
|
Beta Was this translation helpful? Give feedback.
-
|
1. 동시성 프로그래밍이 필요한 이유를 설명하세요. 다음 중 올바른 설명은 무엇인가요? a) CPU의 자원을 최대한 활용하기 위해 ✔️ b) 사용자의 작업을 순차적으로 처리하기 위해 c) 프로그램의 복잡도를 줄이기 위해 d) 메모리 사용량을 줄이기 위해 ,, 얘도 일부분 맞지 않나 싶어요? 2. 동시성과 병렬성(Parallelism)의 차이를 간단히 설명하고, 각각의 장단점을 비교하세요. 동시성 < 병렬성 (범위가 더큼) 둘 다 ‘여러 작업을 동시에 하는 것’에 초점 but 동시성은 컨텍스트 스위칭을 통해 동시에 실행되는 것처럼 보이게 하는 것 → 이것이 바로 GCD 3. 다음 상황에서 동시성 프로그래밍이 필요한 이유를 설명하고, 개선 방법을 코드로 작성하세요.
let semaphore = DispatchSemaphore(value: 0)
// 초기 상태에서는 작업을 허용하지 않음
// 모든 작업 wait
for url in urls { // urls 3개로 가정
network(url: url) { data in
// 데이터 처리
semaphore.signal() // 작업 완료
}
semaphore.wait() // 작업 대기
}4. 다음 코드의 출력 순서를 예측하고, 그 이유를 설명하세요. DispatchQueue.global(qos: .userInitiated).async {
print("Task 1")
DispatchQueue.main.async {
print("Task 2")
}
}
print("Task 3")
"""
출력
Task 3
Task 1
Task 2
메인스레드 즉시 실행
비동기 작업 진행
task3 작업 끝 -> 메인스레드 작업 실행 (메인스레드는 하나여서 serial)
"""5. 다음 코드의 문제점을 찾고, 올바른 수정 방법을 제시하세요. func updateUserInterface() {
DispatchQueue.~~global()~~main.async {
// UI 업데이트 코드
label.text = "Updated"
}
}6. 아래 코드에서 let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
// 네트워크 작업 시뮬레이션
sleep(2)
print("Network Task 1 completed")
group.leave()
}
group.enter()
DispatchQueue.global().async {
// 이미지 다운로드 시뮬레이션
sleep(3)
print("Image Download completed")
group.leave()
}
group.notify(queue: .main) {
print("All tasks completed")
}
"""
Network Task 1 이 완료되고
Image Download 가 완료되고
실행됨
"""7. 다음 코드의 실행 결과와 let workItem = DispatchWorkItem {
for i in 1...5 {
print("Working: \(i)")
sleep(1)
}
}
DispatchQueue.global().async(execute: workItem)
// 2초 후 작업 취소
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
workItem.cancel()
}
"""
2초후에 작업이 취소? 빈약한 취소..라서 해당 코드에서는 전체 for문 돈다 -> isCancelled? 활용
Working: 1
Working: 2
Working: 3
Working: 4
Working: 5
"""8. 다음 시나리오를 해결하는 코드를 작성하세요:
힌트: let urls = [
"https://github.com/IQAndreas/sample-images/blob/gh-pages/100-100-color/00.jpg?raw=true",
"https://github.com/IQAndreas/sample-images/blob/gh-pages/100-100-color/01.jpg?raw=true",
"https://github.com/IQAndreas/sample-images/blob/gh-pages/100-100-color/02.jpg?raw=true"
]
// 최대 3개 동시 다운로드
let downloadQueue = DispatchSemaphore(value: 3)
let group = DispatchGroup()
func downloadFile(_ url: URL) {
group.enter()
semaphore.wait()
DispatchQueue.global().async {
defer {
semaphore.signal()
group.leave()
}
// 다운로드 코드
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error, data == nil {
return
}
}
task.resume()
}
}
for url in urls {
downloadFile(url)
}
group.notify(queue: .main) {
print("다운로드 완료")
}9. 다음 클로저에서 발생할 수 있는 메모리 순환 참조를 해결하는 코드를 작성하세요. class NetworkManager {
func fetchData(completion: @escaping () -> Void) {
DispatchQueue.global().async { **[weak self] in // 얘 추가해야해**
// 네트워크 작업
self**?**.processData() **// ?도 추가~**
completion()
}
}
}10. Race condition의 주요 원인 세 가지를 나열하고, 각각의 문제를 해결할 수 있는 동기화 메커니즘을 제시하세요.
해결법
11. 다음 코드에서 발생할 수 있는 문제를 설명하고, 이를 해결하기 위한 방법을 제안하세요: var sharedCounter = 0
let queue = DispatchQueue.global(qos: .userInitiated)
for _ in 1...10 {
queue.async {
sharedCounter += 1
}
}
print("Final Counter: \(sharedCounter)")
"""
Race condition으로 데이터 제대로 덧셈 안됨.
queue.async 제외하면 돼 ㅎ
"""12. 다음 코드에서 Deadlock이 발생하는 이유를 설명하고, 이를 방지하기 위한 방법을 제안하세요: let lock1 = NSLock()
let lock2 = NSLock()
DispatchQueue.global().async {
lock1.lock()
sleep(1)
lock2.lock()
print("Task 1 completed")
lock2.unlock()
lock1.unlock()
}
DispatchQueue.global().async {
lock2.lock()
sleep(1)
lock1.lock()
print("Task 2 completed")
lock1.unlock()
lock2.unlock()
}
"""
비동기1, 2 단락에서
이미 lock한 애를 다시 lock처리
"""13. Deadlock이 발생하기 위해 필요한 네 가지 조건(Mutual Exclusion, Hold and Wait, No Preemption, Circular Wait)을 간단히 설명하세요.
14. Deadlock을 방지하기 위한 일반적인 전략 두 가지를 제시하고, 각각의 장단점을 설명하세요.
15. Priority Inversion이 발생할 수 있는 상황을 설명하세요:
let highPriorityQueue = DispatchQueue.global(qos: .userInteractive)
let lowPriorityQueue = DispatchQueue.global(qos: .background)
let semaphore = DispatchSemaphore(value: 1)
lowPriorityQueue.async {
semaphore.wait()
sleep(3)
print("Low priority task completed")
semaphore.signal()
}
highPriorityQueue.async {
semaphore.wait()
print("High priority task completed")
semaphore.signal()
}16. iOS에서 GCD(Grand Central Dispatch)를 사용할 때 Priority Inversion 문제가 발생하지 않도록 하기 위해 개발자가 따라야 할 권장 사항은 무엇인가요?
17. 다음 코드에서 Dispatch Barrier를 활용하여 위 코드를 수정하고, 수정된 코드가 thread-safe한 이유를 설명하세요. class SharedResource {
private var data: [String] = []
private let queue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
~~func addItem(_ item: String) {
queue.async {
self.data.append(item)
}
}~~
**func addItem(_ item: String) {
queue.async(flags: .barrier) {
self.data.append(item)
}**
// barrier flags를 사용하여 해당 부분은 serial로 동작하게끔 처리
func getItems() -> [String] {
return queue.sync {
return self.data
}
}
}
let resource = SharedResource()
DispatchQueue.global().async {
resource.addItem("Item 1")
}
DispatchQueue.global().async {
print(resource.getItems())
}18. 아래 코드는 AccessToken과 RefreshToken을 관리하는 클래스입니다. 이 코드를 thread-safe하게 수정하세요. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
iOS Dispatch 예제 문제
1. 동시성 프로그래밍이 필요한 이유를 설명하세요. 다음 중 올바른 설명은 무엇인가요?
a) CPU의 자원을 최대한 활용하기 위해
b) 사용자의 작업을 순차적으로 처리하기 위해
c) 프로그램의 복잡도를 줄이기 위해
d) 메모리 사용량을 줄이기 위해
2. 동시성과 병렬성(Parallelism)의 차이를 간단히 설명하고, 각각의 장단점을 비교하세요.
3. 다음 상황에서 동시성 프로그래밍이 필요한 이유를 설명하고, 개선 방법을 코드로 작성하세요.
4. 다음 코드의 출력 순서를 예측하고, 그 이유를 설명하세요.
5. 다음 코드의 문제점을 찾고, 올바른 수정 방법을 제시하세요.
6. 아래 코드에서
notify블록이 실행되는 시점과 조건을 설명하세요.7. 다음 코드의 실행 결과와
cancel()메서드의 역할을 설명하세요.8. 다음 시나리오를 해결하는 코드를 작성하세요:
힌트:
DispatchSemaphore와DispatchGroup을 활용하세요.9. 다음 클로저에서 발생할 수 있는 메모리 순환 참조를 해결하는 코드를 작성하세요.
10. Race condition의 주요 원인 세 가지를 나열하고, 각각의 문제를 해결할 수 있는 동기화 메커니즘을 제시하세요.
11. 다음 코드에서 발생할 수 있는 문제를 설명하고, 이를 해결하기 위한 방법을 제안하세요:
12. 다음 코드에서 Deadlock이 발생하는 이유를 설명하고, 이를 방지하기 위한 방법을 제안하세요:
13. Deadlock이 발생하기 위해 필요한 네 가지 조건(Mutual Exclusion, Hold and Wait, No Preemption, Circular Wait)을 간단히 설명하세요.
14. Deadlock을 방지하기 위한 일반적인 전략 두 가지를 제시하고, 각각의 장단점을 설명하세요.
15. Priority Inversion이 발생할 수 있는 상황을 설명하세요:
16. iOS에서 GCD(Grand Central Dispatch)를 사용할 때 Priority Inversion 문제가 발생하지 않도록 하기 위해 개발자가 따라야 할 권장 사항은 무엇인가요?
17. 다음 코드에서 Dispatch Barrier를 활용하여 위 코드를 수정하고, 수정된 코드가 thread-safe한 이유를 설명하세요.
18. 아래 코드는 AccessToken과 RefreshToken을 관리하는 클래스입니다. 이 코드를 thread-safe하게 수정하세요.
Beta Was this translation helpful? Give feedback.
All reactions