| ์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- ๋ฐฑ์ค
- RxSwift
- Self
- Swift
- ios
- ํด๋ก์
- View
- weak
- ๋ฆฌ์คํธ๋ทฐ
- Task
- SwiftUI
- ํ๋๊ทธ๋จผํธ
- ์ต์ ๋
- ์๊ณ ๋ฆฌ์ฆ
- ํ์ด
- ํด์
- ์ฐจ์ด
- ์๋ช ์ฃผ๊ธฐ
- ์ค์ํํธ
- concurrency
- ์๋ฐ
- ์๋๋ก์ด๋
- ํ๋ก๊ทธ๋๋จธ์ค
- ์ฐ์ฐ์
- rx
- ํ๋กํผํฐ
- Subject
- ๊ตฌ์กฐ์ฒด
- observable
- async
- Today
- Total
study record
[Swift Concurrency] Task๋ฅผ killํ๋ฉด ์ด๋ป๊ฒ ๋๋? ๋ณธ๋ฌธ
[Swift Concurrency] Task๋ฅผ killํ๋ฉด ์ด๋ป๊ฒ ๋๋?
asong 2025. 12. 15. 18:29์ฐธ๊ณ ํ Medium ๊ธ ๐ฝ
Swift Concurrency์์ Task ์ทจ์(Cancelation)๊ฐ ๋์ํ๋ ๋ฐฉ์ ์ ๋ฆฌ
Swift Concurrency์ Task.cancel()์ ๋ง์ ๊ฐ๋ฐ์๋ค์ด ์คํดํ๊ณ ์๋ ๋ถ๋ถ ์ค ํ๋์ ๋๋ค.
์๋ ๋ด์ฉ์ Swift์์ Task ์ทจ์๊ฐ ์ค์ ๋ก ์ด๋ป๊ฒ ๋์ํ๋์ง,
๊ทธ๋ฆฌ๊ณ ์ทจ์๊ฐ ์ ํ๋๋ ๊ธฐ์ค์ ์ ๋ฆฌํ ๊ฒ์ ๋๋ค.
1. Task.cancel()์ Task๋ฅผ “์ฃฝ์ด๋(kill)” ๊ฒ์ด ์๋๋๋ค
Swift์์ Task๋ ์ฆ์ ์ข ๋ฃ๋์ง ์์ต๋๋ค.
์ทจ์๋ ๋ค์๊ณผ ๊ฐ์ ํน์ง์ ๊ฐ์ง๋๋ค:
- Task๋ ์ทจ์๋๋ฉด ์ฆ์ ์ข ๋ฃ๋์ง ์๊ณ ,
- “์ทจ์๋จ(canceled) ์ํ๋ก ํ์”๋ง ๋ฉ๋๋ค.
- Task ๋ด๋ถ์์ ์ทจ์ ์ฌ๋ถ๋ฅผ ํ์ธํ๊ฑฐ๋(Task.isCancelled)
- ์ทจ์ ๊ฐ๋ฅํ await ์ง์ ์ ๋ง๋๊ธฐ ์ ๊น์ง๋ ๊ณ์ ์คํ๋ฉ๋๋ค.
์์
let task = Task {
print("Start")
try await Task.sleep(nanoseconds: 2_000_000_000)
print("End")
}
task.cancel()
์ ์ฝ๋์์ ์ทจ์ํด๋ "Start"๋ ์ถ๋ ฅ๋๋ฉฐ,
์ ์๊ธฐ(sleep) ์ค์ CancellationError๊ฐ ๋ฐ์ํด์ผ Task๊ฐ ์ข ๋ฃ๋ฉ๋๋ค.
2. ์ทจ์๋ ์๋์ผ๋ก ์ฝ๋ ์ ์ฒด์ ์ ํ๋์ง ์์ต๋๋ค
์๋์ ๊ฐ์ ์คํด๊ฐ ํํฉ๋๋ค:
“Task๋ฅผ ์ทจ์ํ๋ฉด ๋ด๋ถ์์ ํธ์ถํ๋ ํจ์๋ ๋ชจ๋ ์๋์ผ๋ก ์ทจ์๋๋ค?”
→ ๊ทธ๋ ์ง ์์ต๋๋ค.
doSomething() ๊ฐ์ ํจ์๊ฐ ์ทจ์ ์ฌ๋ถ๋ฅผ ์ฒดํฌํ์ง ์๊ฑฐ๋
์ทจ์ ๊ฐ๋ฅํ await๋ฅผ ํธ์ถํ์ง ์์ผ๋ฉด,
Task๋ ์ทจ์ ํ์๋ ๋๊น์ง ์คํ๋ ์ ์์ต๋๋ค.
์
func doSomething() async {
// ์ทจ์ ์ฒดํฌ ์์
// ์ทจ์ ๊ฐ๋ฅํ await ์์
}
์ด ๊ฒฝ์ฐ task.cancel()์ ํธ์ถํด๋ ์์ ์ ๊ณ์ ์ํ๋ฉ๋๋ค.
3. “์ทจ์ ์ ํ(Cancelation Propagation)”๋ Task ์์ฑ ๋ฐฉ์์ ๋ฐ๋ผ ๋ค๋ฆ ๋๋ค
Swift์์ Task๋ ์์ฑ ๋ฐฉ์์ ๋ฐ๋ผ ๋ถ๋ชจ์ ์ทจ์ ์ํ๋ฅผ ์์๋ฐ๊ธฐ๋ ํ๊ณ , ๋ฐ์ง ์๊ธฐ๋ ํฉ๋๋ค.
์ ๋ฆฌ
Task ์์ฑ ๋ฐฉ์์ทจ์ ์ ํ ์ฌ๋ถ
| Task { } | โ ์ ํ ์ ๋จ (๋ ๋ฆฝ์ ์ธ top-level task) |
| Task.detached | โ ์ ํ ์ ๋จ |
| async let | โ ์ ํ๋จ |
| TaskGroup | โ ์ ํ๋จ |
์์
let parentTask = Task {
let child = Task {
await doSomethingSlow()
}
return await child.value
}
parentTask.cancel()
์ ์ฝ๋์์ child๋ ์ทจ์๋์ง ์์ต๋๋ค.
Task { }๋ ๋ถ๋ชจ-์์ ๊ด๊ณ๋ฅผ ๋ง๋ค์ง ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
4. Swift๋ ์ทจ์ ์ ์๋ ์ ๋ฆฌ(cleanup)๋ฅผ ํด์ฃผ์ง ์์ต๋๋ค
์ทจ์๋ ๋จ์ํ CancellationError๋ฅผ ๋์ง๋ ๋์์ด๋ฉฐ,
์๋์ ๊ฐ์ ์์ ์ ์๋์ผ๋ก ๋๋๋ฆฌ๊ฑฐ๋ ๋ณต๊ตฌํ์ง ์์ต๋๋ค.
- ํ์ผ ์ฐ๊ธฐ ์ค๋จ ๋ฐ ๋กค๋ฐฑ
- ๋คํธ์ํฌ ์ ๋ก๋ ์ค๋จ ํ ์ ๋ฆฌ
- DB ํธ๋์ญ์ ๋ณต๊ตฌ
- ์บ์ ์ ๋ฆฌ
์ ๋ฆฌ๋ ๊ฐ๋ฐ์๊ฐ ์ง์ ๊ตฌํํด์ผ ํฉ๋๋ค.
์์
let task = Task {
do {
try await doExpensiveWork()
} catch is CancellationError {
await cleanUp()
}
}
task.cancel()
5. ์ทจ์๊ฐ ํ๋ฅด๋๋ก ํ๋ ค๋ฉด ๋ถํ์ํ Task { } ๋ํ์ ํผํด์ผ ํฉ๋๋ค
์๋ ํจ์๋ ๋ด๋ถ์์ ๋ ๋ฆฝ์ ์ธ top-level task๋ฅผ ์์ฑํฉ๋๋ค:
func fetchUserData() async throws -> User {
let task = Task {
await refreshAuthToken()
return try await networkService.loadUser()
}
return try await task.value
}
→ ์ด ๊ฒฝ์ฐ, ์์ ํธ์ถ์๊ฐ ์ทจ์๋์ด๋ ๋ด๋ถ Task๋ ๊ทธ๋๋ก ์คํ๋ฉ๋๋ค.
ํด๊ฒฐ๋ฒ: Task๋ฅผ ์ฐ์ง ์๊ณ ํจ์ ์์ฒด๋ฅผ async๋ก ์ ์งํ๊ธฐ
func fetchUserData() async throws -> User {
try await refreshAuthToken()
return try await networkService.loadUser()
}
์ด ๋ฐฉ์์ ์์ฐ์ค๋ฝ๊ฒ ํธ์ถ์์ ์ทจ์๊ฐ ์ ํ๋๋ฉฐ,
๋ถํ์ํ Task ์ค์ฒฉ์ ๋ฐฉ์งํฉ๋๋ค.
6. ์ทจ์ ์ ํ๊ฐ ํ์คํ ํจํด: async let
async let์ ์ฌ์ฉํ๋ฉด, ์ทจ์๋ ์๋์ผ๋ก ์์ ์์ ์ ์ ํ๋ฉ๋๋ค.
์์
func fetchData() async throws -> String {
async let name = loadName()
async let age = loadAge()
return "\(try await name), \(try await age)"
}
→ fetchData()๊ฐ ์ทจ์๋๋ฉด
loadName()๊ณผ loadAge()๋ ์๋์ผ๋ก ์ทจ์๋ฉ๋๋ค.
7. ์ ๋ฆฌ: Swift์์ Task ์ทจ์๊ฐ ์ค์ ๋ก ์ผ์ด๋๋ ๋ฐฉ์
Task ์ทจ์ ํ ์ค์ ์ข ๋ฃ๋๋ ค๋ฉด ์๋ ์ค ํ๋๊ฐ ํ์ํฉ๋๋ค:
- Task ๋ด๋ถ์์ Task.isCancelled ์ฒดํฌ
- ์ทจ์ ๊ฐ๋ฅํ await๋ฅผ ๋ง๋ CancellationError๊ฐ ๋ฐ์
- ๊ฐ๋ฐ์๊ฐ ์๋์ผ๋ก ์ทจ์๋ฅผ ์ ํ
- async let ๋๋ TaskGroup์ ํตํด ๋ถ๋ชจ ์ทจ์๊ฐ ์ ํ๋จ
์ ์กฐ๊ฑด์ด ํ๋๋ ์๋ค๋ฉด,
Task๋ ์ทจ์๋๋๋ผ๋ ๋๊น์ง ์คํ๋ฉ๋๋ค.
๊ฒฐ๋ก
Swift Concurrency์ ์ทจ์ ๋ก์ง์ ๋จ์ํ ๊ธฐ๋ฅ์ฒ๋ผ ๋ณด์ด์ง๋ง
์ค์ ๋์์ ๋ค์์ ์ดํดํด์ผ ์ ๋๋ก ๋ค๋ฃฐ ์ ์์ต๋๋ค:
- Task๋ ์ฆ์ “์ฃฝ์ง ์๋๋ค”
- ์ทจ์๋ ์ ํ๋์ง ์์ ์ ์๋ค
- cleanup์ ์๋์ผ๋ก ์ผ์ด๋์ง ์๋๋ค
- async let / TaskGroup์ ์ฌ๋ฐ๋ฅธ ์ทจ์ ์ ํ๋ฅผ ์ ๊ณตํ๋ค
- ๋ถํ์ํ Task wrapping์ ์ทจ์ ๋ฒ๊ทธ๋ฅผ ๋ง๋ ๋ค
Concurrency๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ๋ค๋ฃจ๊ธฐ ์ํด์๋ ์ทจ์์ ๋์ ์๋ฆฌ์ ์ ํ ๊ท์น์ ์ ํํ ์ดํดํ๋ ๊ฒ์ด ํ์์ ์ ๋๋ค.
++) ์์ ์ถ๊ฐ
1. Task ๋ด๋ถ์์ Task.isCancelled ์ฒดํฌํ๋ ์์๋ค
์์ 1 — ๋ฐ๋ณต ์์ ์ค๊ฐ์ ์ทจ์ ์ฌ๋ถ ํ์ธ
let task = Task {
for i in 0..<10 {
if Task.isCancelled {
print("Canceled while iterating. Cleaning up...")
return
}
print("Processing \(i)")
try await Task.sleep(nanoseconds: 300_000_000)
}
}
task.cancel()
์์ 2 — ๋ฌด๊ฑฐ์ด ์ฐ์ฐ ์ค ์ฃผ๊ธฐ์ ์ผ๋ก ์ทจ์ ์ฒดํฌ
func heavyComputation() async {
var result = 0
for i in 0..<100_000 {
if Task.isCancelled {
print("Canceled during computation")
return
}
result += i
}
print("Finished: \(result)")
}
let task = Task {
await heavyComputation()
}
task.cancel()
์์ 3 — ๋คํธ์ํฌ ์ฌ์๋ ๋ก์ง์์ ์ทจ์ ๋ฐ์
func fetchWithRetry() async throws -> Data {
for attempt in 1...3 {
if Task.isCancelled {
throw CancellationError()
}
do {
return try await networkService.fetch()
} catch {
if attempt == 3 { throw error }
}
}
throw URLError(.unknown)
}
2. ์ทจ์ ๊ฐ๋ฅํ await๋ฅผ ๋ง๋ CancellationError๊ฐ ๋ฐ์ํ๋ ์์๋ค
Swift Concurrency์์ ์ผ๋ถ await๋ ์๋์ผ๋ก ์ทจ์๋ฅผ ์ ํํฉ๋๋ค.
๋ํ์ ์ธ ์:
- Task.sleep
- ๋คํธ์ํฌ API (URLSession ๊ธฐ๋ฐ)
- Actor ์ ๊ทผ
- async let ๊ฒฐ๊ณผ ๊ธฐ๋ค๋ฆฌ๊ธฐ
- TaskGroup ๋ด ์์ ๊ธฐ๋ค๋ฆฌ๊ธฐ ๋ฑ
์์ 1 — Task.sleep๋ ์ทจ์ ์ ๋ฐ๋ก CancellationError ๋ฐ์
let task = Task {
print("Start")
try await Task.sleep(nanoseconds: 5_000_000_000)
print("End") // ์คํ๋์ง ์์
}
task.cancel()
์์ 2 — URLSession ์์ฒญ์์ ์ทจ์๊ฐ ๋ฐ์
func load() async throws -> Data {
let url = URL(string: "https://example.com")!
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
let task = Task {
try await load()
}
task.cancel() // URLSession์ด ์๋์ผ๋ก ์ทจ์๋จ → CancellationError ๋ฐ์
์์ 3 — Actor ์ ๊ทผ ์ค ์ทจ์
actor Counter {
var value = 0
func increment() { value += 1 }
}
let counter = Counter()
let task = Task {
try await Task.sleep(nanoseconds: 1_000_000_000)
await counter.increment() // ์ด ์ง์ ์์ ์ทจ์๊ฐ ๊ฐ์ง๋ ์ ์์
}
task.cancel()
3. ๊ฐ๋ฐ์๊ฐ ์๋์ผ๋ก ์ทจ์๋ฅผ ์ ํํ๋ ์์๋ค
์๋ ์ ํ๋, ํ Task๊ฐ ์ทจ์๋์์์ ๊ฐ์งํ๊ณ
๋ด๋ถ์์ ๋ค๋ฅธ ์์ ์๋ ์ทจ์๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ์์ ๋๋ค.
์์ 1 — ์ทจ์ ์ cleanup + ์์ task ์ทจ์
func loadData() async throws {
let child = Task {
try await Task.sleep(nanoseconds: 5_000_000_000)
print("Child finished")
}
do {
try await child.value
} catch is CancellationError {
child.cancel()
print("Canceled and cleaned")
throw CancellationError()
}
}
์์ 2 — ์ฌ๋ฌ ์์ ์ ๋์์ ์คํํ๊ณ ์ทจ์ ์ ๋ชจ๋ ์ค๋จ
func fetchMultiple() async throws {
let t1 = Task { try await loadA() }
let t2 = Task { try await loadB() }
do {
let resultA = try await t1.value
let resultB = try await t2.value
print(resultA, resultB)
} catch is CancellationError {
t1.cancel()
t2.cancel()
throw CancellationError()
}
}
์์ 3 — Operation-style cleanup
let task = Task {
do {
try await longRunningProcess()
} catch is CancellationError {
await rollbackChanges()
throw CancellationError()
}
}
task.cancel()
'Swift > Concurrency' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Swift Concurrency] Optimize with Instruments (0) | 2025.04.28 |
|---|---|
| [Swift Concurrency] Strict Concurrency Checking (0) | 2025.04.28 |
| [Swift Concurrency] AsyncStream, AsyncThrowingStream, Continuation์ด๋? (0) | 2024.01.30 |
| [Swift Concurrency] Intermediate async/await & CheckedContinuation (0) | 2023.06.11 |
| [Swift Concurrency] Custom Asynchronous Sequences With AsyncStream (0) | 2023.05.29 |