Actors

Groot·2022년 12월 17일
0

TIL

목록 보기
113/148
post-thumbnail

TIL

🌱 난 오늘 무엇을 공부했을까?

📌 Swift Concurrency - 2

📍 Actors?

  • 클래스와 달리 Actor는 한 번에 하나의 작업만 변경 가능한 상태에 액세스할 수 있도록 허용하므로 여러 작업의 코드가 동일한 Actor 인스턴스와 상호 작용하는 것이 안전하다.

    • 별도로 실행되는 비동기 코드를 사용할 때 공유해야 하는 정보(var)가 있으면 Race Condition 문제가 생길 수도 있다.
    • 이 문제를 해결하기 위해 기존 GCD는 Semaphore방식을 사용한다.
    • Concurrency에선 Actors 타입을 사용해서 정보를 안전하게 공유한다. (참조타입)
  • 예시 : 비동기로 값을 수정하고 읽어야 하는 상황

import Foundation

class User {
    let id: String
    let name: String
    
    init(id: String, name: String) {
        self.id = id
        self.name = name
    }
}

class UserStorage {
    private var store = [String: User]()
    
    func store(_ id: String) -> User? {
        return store[id]
    }
    
    func save(_ user: User) {
        self.store[user.id] = user
    }
}

let storage = UserStorage()

DispatchQueue.global().async {
    storage.save(User(id: "kim", name: "groot"))
}

DispatchQueue.global().async {
    print(storage.store("kim")?.name)
}
  • 위의 코드를 실행했을 때 nil을 출력.

    • save, store 함수는 비동기적으로 실행되기 때문에 store를 실행하는 시점에는 어떤 User도 없는 경우가 있다.
    • Read와 Update가 동시에 store로 접근하게 되면 런타임 오류가 생길 수 있다.
  • 이 코드를 정상적으로 수행하기 위한 방법

    1. DispatchSemaphore를 사용하는 방법

      let storage = UserStorage()
      let semaphore = DispatchSemaphore(value: 1)
      
      DispatchQueue.global().async {
          semaphore.wait()
          storage.save(User(id: "kim", name: "groot"))
          semaphore.signal()
      }
      
      DispatchQueue.global().async {
          semaphore.wait()
          print(storage.store("kim")?.name)
          semaphore.signal()
      }
  • 이렇게 수정하면 정상적으로 값을 저장하고 읽어올 수 있지만, Actor를 사용하면 Swift에서 Race Condition 문제를 처리해준다. Thread Safe한 상태로 선언이 가능하다.

    actor UserStorage {
        private var store = [String: User]()
    
        func get(_ id: String) -> User? {
            return store[id]
        }
    
        func save(_ user: User) {
            self.store[user.id] = user
        }
    }
    
    let storage = UserStorage()
    
    Task {
        await storage.save(User(id: "kim", name: "groot"))
    }
    
    Task {
        print(await storage.get("kim")?.name)
    }
  • Actor는 메서드 실행 시 기본적으로 await 키워드를 사용한다.

  • await로 실행해주게 되면 다음 await는 이전에 실행된 await가 끝나고 나서 다음 작업이 가능하기 때문에 동시에 특정 값에 접근하는 문제가 생기지 않는다. Thread Safe하다

https://www.youtube.com/watch?v=5LKbL-I-CYY
https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html

profile
I Am Groot

0개의 댓글