Swift Concurrency 6편 - @Sendable 과 Data Race Safety는?

김재형·2024년 6월 23일
1
post-custom-banner

시작하기에 앞서

1편부터 이어지는 내용임으로 1편부터 봐주시길 권장드립니다. 시작하겠습니다.

@Sendable

@Sendable 이란 무엇일까요?

직역으론 "데이터 레이스의 위험을 초래하지 않고 임의의 동시 컨텍스트에서 값을 공유할 수 있는 스레드 안전 유형입니다." 라고 되어 있는데 쉽게 한번 핵심적인 부분 을 정리해보죠.

  • 동시성 모델에 사용되는 것이구나!
  • 스레드가 안전해 진다는 구나!

다시 정리해서 -> 클로저가 다른 스레드나 큐로 안전하게 전달될수 있음을 나타내는구나! 라고 생각하시면 됩니다. 또한, @Sendable 클로저는 클로저 내에서 동기화 되지 않는 공유인 상태를 수정하지 못하게 합니다.

func performTask(_ task: @Sendable @escaping () async -> Void) {
    Task {
        await task()
    }
}

performTask {
    print("@Sendable closure 라구욧")
}

더욱 깊은 내용은 Sandable 본편에서 다루어 보도록 합시다.

Data Race Safety 는 뭐에요?

다시 돌아와서 @Sendable 클로저 내부는 동기화 되지 않은 공유 상태 Context 에서 변경 가능한 변수를
캡처하지 못하도록 하는데, 이유가 무엇이였을까요?
-> 값타입(String, Int...), 동기화를 구현하는 Class 혹은 Actor 객체들은 타 쓰레드도 접근 가능하게 되어 있습니다. 즉 그것을 막고자 만들게 된것이죠.

import Foundation

func fetchData(url: URL) async throws -> Data {
    let (data, _) = try await URLSession.shared.data(from: url)
    return data
}

func performAsyncTask() async {
    let url = URL(string: "https://KingJaeHyung.com")!
    do {
        let data = try await fetchData(url: url)
        if let json = try? JSONSerialization.jsonObject(with: data, options: []) {
            print("JSON: \(json)")
        }
    } catch {
        print("Error: \(error)")
    }
}

// 비동기 함수 호출
Task {
    await performAsyncTask()
}

4편의 코드를 가져와 보았는데 혹시 알고 계셨나요?
Swift Concurrency는 @Sendable을 직접 적지 않았지만, 내부적으로 @Sendable를 따르도록 되어있습니다.

func fetchMultipleData(urls: [URL]) async throws -> [Data] {
    return try await withThrowingTaskGroup(of: Data.self) { group -> [Data] in
        var results = [Data]()
        
        // 각 URL 작업을 그룹에 추가
        for url in urls {
            group.addTask {
                return try await fetchData(url: url)
            }
        }
        
        // 각 비동기 작업의 결과를 수집
        for try await result in group {
            results.append(result)
        }
        
        return results
    }
}

해당 코드도 동일하죠. (5편참고)

그래서 Data Race Safety 하단게 뭔뎅

위에 코드를 보면서 다시 생각해 보죠.
여러 비동기 작업을 그룹으로 묶고난후,
안전하게 관리하기 위해 withThrowingTaskGroup을 사용합니다.
group.addTask 를 사용하여 각 URL에 대해 fetchData(url:)를 호출하며, 각 작업이
독립적으로 실행되게 합니다.
for try await result in group 구문을 통해, 각 작업의 결과를 안전 하게 가져옵니다.
각 그룹 내의 작업이 완료될때까지 기다리며, 결과를 순차적 처리합니다.
이를 Data Race Safety 한것 이라고 하는겁니다.

마무리 하며

다음시간에는 "Unstructured tasks", "Detached tasks" 에 대해서 정리하도록 하겠습니다!
다음에 뵙도록 하죠!

profile
IOS 개발자 새싹이
post-custom-banner

2개의 댓글

comment-user-thumbnail
2024년 6월 28일

멋지다..

1개의 답글