1편부터 이어지는 내용임으로 1편부터 봐주시길 권장드립니다. 시작하겠습니다.
@Sendable
이란 무엇일까요?
직역으론 "
데이터 레이스의 위험을 초래하지 않고 임의의 동시 컨텍스트에서 값을 공유할 수 있는 스레드 안전 유형입니다.
" 라고 되어 있는데 쉽게 한번 핵심적인 부분 을 정리해보죠.
다시 정리해서 -> 클로저가 다른 스레드나 큐로 안전하게 전달될수 있음을 나타내는구나! 라고 생각하시면 됩니다. 또한, @Sendable 클로저는 클로저 내에서 동기화 되지 않는 공유인 상태를 수정하지 못하게 합니다.
func performTask(_ task: @Sendable @escaping () async -> Void) {
Task {
await task()
}
}
performTask {
print("@Sendable closure 라구욧")
}
더욱 깊은 내용은 Sandable 본편에서 다루어 보도록 합시다.
다시 돌아와서 @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편참고)
위에 코드를 보면서 다시 생각해 보죠.
여러 비동기 작업을 그룹으로 묶고난후,
안전하게 관리하기 위해withThrowingTaskGroup
을 사용합니다.
group.addTask
를 사용하여 각 URL에 대해fetchData(url:)
를 호출하며, 각 작업이
독립적으로 실행되게 합니다.
for try await result in group
구문을 통해, 각 작업의 결과를안전
하게 가져옵니다.
각 그룹 내의 작업이 완료될때까지 기다리며, 결과를 순차적 처리합니다.
이를Data Race Safety
한것 이라고 하는겁니다.
다음시간에는 "Unstructured tasks", "Detached tasks" 에 대해서 정리하도록 하겠습니다!
다음에 뵙도록 하죠!
멋지다..