신속한 동시성

Feb 14 2023
소개: Swift에서는 애플리케이션이 사용자 인터페이스를 차단하지 않고 장기 실행 작업을 처리할 수 있어야 하므로 비동기 프로그래밍이 점점 더 중요해지고 있습니다. 최신 버전의 Swift, Swift 5.

소개:

Swift에서는 애플리케이션이 사용자 인터페이스를 차단하지 않고 장기 실행 작업을 처리할 수 있어야 하므로 비동기 프로그래밍이 점점 더 중요해지고 있습니다. Swift의 최신 버전인 Swift 5.5는 비동기식 프로그래밍을 보다 쉽고 표현력 있게 만드는 여러 가지 새로운 기능을 도입했습니다. 이 기사에서는 새로운 async/await 구문, 작업, async let, 작업 그룹, 행위자 및 전역 행위자를 살펴보고 이러한 각 기능이 더 효율적이고 읽기 쉽고 유지 관리 가능한 코드를 작성하는 데 어떻게 도움이 되는지 살펴보겠습니다.

  1. 비동기/대기:

Async/await를 사용하면 완료 핸들러를 사용하지 않고도 값을 반환하는 비동기 함수를 작성할 수 있습니다. async 키워드는 함수를 비동기로 표시하는 데 사용되며 await 키워드는 비동기 작업이 완료될 때까지 대기하는 데 사용됩니다. 다음은 예입니다.

func fetchImage() async throws -> UIImage {
 let url = URL(string: "https://example.com/image.jpg")!
 let (data, response) = try await URLSession.shared.data(from: url)
 guard (response as? HTTPURLResponse)?.statusCode == 200 else {
 throw NetworkError.invalidResponse
 }
 return UIImage(data: data)!
}

2 . 작업:

Swift 5.5에서 작업은 다른 작업과 동시에 실행할 수 있는 작업 단위를 나타낼 수 있는 새로운 동시성 프리미티브입니다. 작업은 동기 및 비동기 작업을 모두 수행하는 데 사용할 수 있으며 더 이상 필요하지 않은 경우 취소할 수 있습니다.

작업은 Task.init 함수를 사용하여 생성되며 Task.run 함수를 사용하여 실행할 수 있습니다. 다음은 예입니다.

let task = Task {
    print("Hello, world!")
}
task.run()

3. 비동기 렛:

Async let은 변수를 비동기식으로 선언하고 초기화할 수 있게 해주는 Swift 5.5의 새로운 기능입니다. 이는 변수를 사용하기 전에 비동기 작업의 결과를 기다려야 할 때 유용합니다.

Async let은 let 키워드 앞에 async 키워드를 붙여 사용합니다. 다음은 예입니다.

func fetchImage() async throws -> UIImage {
    let url = URL(string: "https://example.com/image.jpg")!
    let (data, _) = try await URLSession.shared.data(from: url)
    let image async = try UIImage(data: data)
    return image
}

4. 작업 그룹:

작업 그룹은 여러 작업을 동시에 실행하고 모든 작업이 완료될 때까지 기다릴 수 있는 Swift 5.5의 새로운 기능입니다. 작업 그룹은 동시에 실행할 수 있는 독립적인 작업이 많고 계속하기 전에 모든 작업이 완료될 때까지 기다려야 하는 경우에 유용합니다.

작업 그룹은 TaskGroup.init 함수를 사용하여 생성되며 TaskGroup.add 함수를 사용하여 그룹에 작업을 추가할 수 있습니다. 여기

let group = TaskGroup<Int, Error>()
for i in 1...10 {
    group.addTask {
        await Task.sleep(1_000_000_000)
        return i * 2
    }
}

do {
    let results = try await group.waitForAll()
    print(results)
} catch {
    print("An error occurred: \(error)")
}

5. 배우:

액터는 보다 선언적인 방식으로 스레드로부터 안전한 코드를 작성할 수 있게 해주는 Swift 5.5의 새로운 기능입니다. 액터는 한 번에 하나의 작업에서만 액세스할 수 있는 개체이며 메시지 전달을 사용하여 다른 액터와 통신합니다. 액터를 사용하여 데이터 경합 및 교착 상태와 같은 일반적인 동시성 문제를 방지할 수 있습니다.

다음은 예입니다.

actor BankAccount {
    private var balance: Int

    init(balance: Int) {
        self.balance = balance
    }

    func deposit(amount: Int) {
        balance += amount
    }

    func withdraw(amount: Int) -> Bool {
        if amount <= balance {
            balance -= amount
            return true
        } else {
            return false
        }
    }
}

let account = BankAccount(balance: 100)
Task.detached {
    account.deposit(amount: 50)
}
Task.detached {
    if account.withdraw(amount: 75) {
        print("Withdrawal succeeded")
    } else {
        print("Withdrawal failed")
    }
}

6 . 글로벌 액터:

전역 액터는 Swift 5.5의 새로운 기능으로, 보다 선언적인 방식으로 동시 코드를 작성할 수 있습니다. 전역 액터는 시스템의 모든 작업에서 액세스할 수 있는 특수한 유형의 액터입니다. 전역 액터를 사용하면 각각에 대해 사용자 정의 액터를 정의할 필요 없이 특정 유형이나 함수가 항상 스레드로부터 안전한 방식으로 액세스되도록 할 수 있습니다.

다음은 예입니다.

@globalActor
struct Logger {
    static let shared = Logger()

    func log(_ message: String) {
        print(message)
    }
}

@Logger func logMessage() {
    print("Hello, world!")
}

Task.detached {
    logMessage()
}

결론:

Swift 5.5는 동시 및 비동기 코드를 더 쉽게 작성할 수 있는 몇 가지 새로운 기능을 도입했습니다. Async/await는 비동기 함수 작업을 위한 구문을 단순화하는 반면, Task 및 TaskGroup은 여러 동시 작업을 관리하는 보다 유연한 방법을 제공합니다. 마지막으로 전역 액터를 사용하면 특정 유형이나 함수가 항상 스레드로부터 안전한 방식으로 액세스되도록 쉽게 할 수 있습니다. 이러한 기능을 통해 개발자는 특히 복잡한 동시 시스템으로 작업할 때 보다 효율적이고 안정적인 코드를 쉽게 작성할 수 있습니다.