Bibi's DevLog ๐Ÿค“๐ŸŽ

[Swift Language Guide] Concurrency (async/await, task, actor) ๋ณธ๋ฌธ

๐Ÿ“ฑ๐ŸŽ iOS/๐Ÿ•Š Swift

[Swift Language Guide] Concurrency (async/await, task, actor)

๋น„๋น„ bibi 2023. 4. 16. 21:33

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/
์œ„ ๊ณต์‹๋ฌธ์„œ๋ฅผ ๋ฒˆ์—ญํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

๋™์‹œ์„ฑ : ๋น„๋™๊ธฐ ์ž‘์—… ์ˆ˜ํ–‰ํ•˜๊ธฐ.

๋ณ‘๋ ฌ(parallel) ๋˜๋Š” ๋น„๋™๊ธฐ(asynchronous) ์ฝ”๋“œ๋Š” ๋ณต์žก๋„ ์ฆ๊ฐ€๋ผ๋Š” ๋น„์šฉ์„ ๊ฐ€์ ธ์˜จ๋‹ค.
Swift์—์„œ๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„ ๊ฒ€์‚ฌ๊ฐ€ ๊ฐ€๋Šฅํ•œ ๋ฐฉ์‹์œผ๋กœ ๊ฐœ๋ฐœ์ž์˜ ์˜๋„๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค - ์˜ˆ๋ฅผ ๋“ค์–ด actor๋ฅผ ์‚ฌ์šฉํ•ด mutable state์— ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
Swift๋Š” ์–ธ์–ด ์ˆ˜์ค€์—์„œ ๋น„๋™๊ธฐ ์ฝ”๋“œ์˜ ๋ฌธ์ œ๋ฅผ ์ปดํŒŒ์ผ ํƒ€์ž„์— ์žก์„ ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š”๋‹ค.

Swift์˜ ๋™์‹œ์„ฑ ๋ชจ๋ธ์€ ์“ฐ๋ ˆ๋“œ ์œ„์— ๊ตฌ์ถ•๋˜์ง€๋งŒ, ์“ฐ๋ ˆ๋“œ์™€ ์ง์ ‘ ์ƒํ˜ธ์ž‘์šฉํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

completion handler๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์กด ์ฝ”๋“œ

listPhotos(inGallery: "Summer Vacation") { photoNames in
    let sortedNames = photoNames.sorted()
    let name = sortedNames[0]
    downloadPhoto(named: name) { photo in
        show(photo)
    }
}

ํด๋กœ์ €๋ฅผ ์ค‘์ฒฉํ•ด ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๊ฐ€๋…์„ฑ์ด ์•ˆ ์ข‹์•„์ง€๊ณ , ๋ณต์žกํ•ด์ง„๋‹ค.

๋น„๋™๊ธฐ ํ•จ์ˆ˜์˜ ์ •์˜ ๋ฐ ํ˜ธ์ถœ

๋น„๋™๊ธฐ ํ•จ์ˆ˜/๋ฉ”์„œ๋“œ : ์‹คํ–‰ ์ค‘ ์ผ์‹œ์ •์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ํŠน์ˆ˜ํ•œ ์ข…๋ฅ˜์˜ ํ•จ์ˆ˜/๋ฉ”์„œ๋“œ๋ฅผ ์˜๋ฏธํ•จ.
๋ณดํ†ต์˜ ํ•จ์ˆ˜(๋™๊ธฐ์‹) ์€ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰๋˜๊ฑฐ๋‚˜, ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜, ๋ฐ˜ํ™˜๋˜์ง€ ์•Š๋Š” ์‹์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค. ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋„ ์ด๋Ÿฐ ์‹์œผ๋กœ ๋™์ž‘ํ•˜์ง€๋งŒ, ๊ฐ€์žฅ ํฐ ์ฐจ์ด๋Š” ์–ด๋–ค ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ์ผ์‹œ์ •์ง€๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.

๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ์„ ์–ธ๊ณผ -> ์‚ฌ์ด์— async ํ‚ค์›Œ๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
์—๋Ÿฌ๋ฅผ ๋˜์ง€๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋Š” async throws ์ˆœ์œผ๋กœ ์ž‘์„ฑํ•œ๋‹ค.

func listPhotos(inGallery name: String) async -> [String] {
    let result = // ... some asynchronous networking code ...
    return result
}

๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰์ด ์ค‘์ง€๋œ๋‹ค.
์ผ์‹œ์ •์ง€๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์„ ํ˜ธ์ถœํ•  ๋•Œ์—๋Š” ์•ž์— await ํ‚ค์›Œ๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. (์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ, try ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๋‹ค)

์‹คํ–‰ ํ๋ฆ„์ด ์ผ์‹œ์ •์ง€ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์˜ค์ง ๋‹ค๋ฅธ ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ–ˆ์„ ๋•Œ์ด๋‹ค - ์ผ์‹œ์ •์ง€๋Š” ์ ˆ๋Œ€ ์•”๋ฌต์ ์ด๊ฑฐ๋‚˜ ์„ ์ ์ ์ด์ง€ ์•Š๋‹ค. ์ฆ‰ ๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ์ผ์‹œ์ •์ง€ ์ง€์ ์ด await ๋กœ ํ‘œ์‹œ๋œ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜ ์ฝ”๋“œ๋Š” ๊ฐค๋Ÿฌ๋ฆฌ์˜ ๋ชจ๋“  ์‚ฌ์ง„์˜ ์ด๋ฆ„์„ ๊ฐ€์ ธ์˜จ ๋‹ค์Œ ์ฒซ ๋ฒˆ์งธ ์‚ฌ์ง„์„ ๋ณด์—ฌ์ค€๋‹ค:

let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
  • listPhotos(inGallery:) ์™€ downloadPhoto(named:) ํ•จ์ˆ˜๋Š” ๋ชจ๋‘ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ํ•„์š”๋กœ ํ•˜๋ฏ€๋กœ, ์ž‘์—… ์™„๋ฃŒ์— ์ƒ๋Œ€์ ์œผ๋กœ ๊ธด ์‹œ๊ฐ„์ด ์†Œ์š”๋œ๋‹ค.
  • ๋‘ ํ•จ์ˆ˜๋ฅผ async ๋ฅผ ํ†ตํ•ด ๋น„๋™๊ธฐ๋กœ ๋งŒ๋“ค๋ฉด, ์‚ฌ์ง„์ด ์ค€๋น„๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ์•ฑ์˜ ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋“ค์ด ๋™์ž‘ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ„ ์ฝ”๋“œ์˜ ์‹คํ–‰ ์ˆœ์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค:

  1. ์ฒซ ๋ฒˆ์จฐ ์ค„๋ถ€ํ„ฐ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜์–ด, ์ฒซ ๋ฒˆ์จฐ await ๊นŒ์ง€ ์‹คํ–‰๋œ๋‹ค. ๊ทธ๊ฒƒ์€ listPhotos(inGallery:) ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ๊ทธ ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ์‹คํ–‰์„ ์ผ์‹œ์ •์ง€ํ•œ๋‹ค.
  2. ์ด ์ฝ”๋“œ์˜ ์‹คํ–‰์ด ์ค‘์ง€๋œ ๋™์•ˆ, ๊ฐ™์€ ํ”„๋กœ๊ทธ๋žจ์— ์žˆ๋Š” ๋‹ค๋ฅธ ๋ช‡๋ช‡ ๋™์‹œ์„ฑ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.
    1. ์˜ˆ๋ฅผ ๋“ค์–ด, ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์ด ์ƒˆ ํฌํ†  ๊ฐค๋Ÿฌ๋ฆฌ์˜ ๋ชฉ๋ก์„ ๊ณ„์† ์—…๋ฐ์ดํŠธํ•  ๊ฒƒ์ด๋‹ค.
    2. ๊ทธ ์ฝ”๋“œ๋Š” await ๋กœ ํ‘œ์‹œ๋œ ๋‹ค์Œ ์ผ์‹œ์ •์ง€ ์ง€์ ์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€, ๋˜๋Š” ์™„๋ฃŒ๋  ๋–„๊นŒ์ง€ ์‹คํ–‰๋  ๊ฒƒ์ด๋‹ค.
  3. listPhotos(inGallery:) ๊ฐ€ ๋ฐ˜ํ™˜๋œ ํ›„, ์ด ์ฝ”๋“œ๋Š” ๊ทธ ์ง€์ ๋ถ€ํ„ฐ ์‹คํ–‰์„ ๊ณ„์†ํ•  ๊ฒƒ์ด๋‹ค. photoNames ์— ๋ฐ˜ํ™˜๋œ ๊ฐ’์„ ํ• ๋‹นํ•  ๊ฒƒ์ด๋‹ค.
  4. sortedNames ์™€ name ์— ์ •์˜๋œ ์ฝ”๋“œ๋Š” ์ผ๋ฐ˜์ ์ธ ๋™๊ธฐ ์ฝ”๋“œ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์–ด๋–ค ์ผ์‹œ์ •์ง€๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.
  5. ๋‹ค์Œ await ๊ฐ€ downloadPhoto(named:) ํ•จ์ˆ˜ ํ˜ธ์ถœ์— ํ‘œ์‹œ๋˜์–ด ์žˆ๋‹ค. ์ด ์ฝ”๋“œ๋Š” ์ด ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰์„ ๋˜๋‹ค์‹œ ์ผ์‹œ์ •์ง€ํ•  ๊ฒƒ์ด๋ฉฐ, ๋‹ค๋ฅธ ๋™์‹œ์„ฑ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋  ๊ธฐํšŒ๋ฅผ ์ค„ ๊ฒƒ์ด๋‹ค.
  6. downloadPhoto(named:) ๊ฐ€ ๋ฐ˜ํ™˜๋œ ํ›„์—, ๊ทธ ๋ฐ˜ํ™˜๊ฐ’์€ photo ์— ํ• ๋‹น๋˜๋ฉฐ show(_:) ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ์— ์•„๊ทœ๋จผํŠธ๋กœ์„œ ์ „๋‹ฌ๋  ๊ฒƒ์ด๋‹ค.

์ฝ”๋“œ ๋‚ด์—์„œ ์ผ์‹œ์ •์ง€๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์€ await ๋กœ ํ‘œ์‹œ๋˜์–ด, ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ๋˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ํ˜„์žฌ ์ฝ”๋“œ ์กฐ๊ฐ์ด ์‹คํ–‰์„ ์ค‘์ง€์‹œํ‚ฌ ์ˆ˜ ์žˆ์Œ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋˜ํ•œ ์“ฐ๋ ˆ๋“œ ์–‘๋ณด(yielding the thread) ๋ผ๊ณ ๋„ ๋ถˆ๋ฆฌ๋Š”๋ฐ, ๊ทธ ์ด์œ ๋Š” Swift๊ฐ€ ๋’ท๋‹จ์—์„œ ํ˜„์žฌ ์“ฐ๋ ˆ๋“œ์˜ ์ฝ”๋“œ ์‹คํ–‰์„ ์ค‘์ง€์‹œํ‚ค๊ณ  ๊ทธ ์Šค๋ ˆ๋“œ์˜ ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ๋Œ€์‹  ์‹คํ–‰์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

await ๋กœ ํ‘œ์‹œ๋œ ์ฝ”๋“œ๋Š” ์‹คํ–‰์„ ์ผ์‹œ์ •์ง€ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ, ํ”„๋กœ๊ทธ๋žจ์˜ ํŠน์ • ์œ„์น˜์—์„œ๋งŒ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค :

  • ๋น„๋™๊ธฐ ํ•จ์ˆ˜/๋ฉ”์„œ๋“œ/ํ”„๋กœํผํ‹ฐ์˜ body์—์„œ
  • @main ์œผ๋กœ ํ‘œ์‹œ๋œ ๊ตฌ์กฐ์ฒด, ํด๋ž˜์Šค, ์—ด๊ฑฐํ˜•์˜ static main() ํ•จ์ˆ˜์—์„œ
  • Unstructured Concurrency์˜ ๋‚ด์šฉ๊ณผ ๊ฐ™์ด, ๊ตฌ์กฐํ™”๋˜์ง€ ์•Š์€ child task์—์„œ

์ค‘๋‹จ๋  ์ˆ˜ ์žˆ๋Š” ์ง€์  ์‚ฌ์ด์˜ ์ฝ”๋“œ๋Š”, ๋‹ค๋ฅธ ๋™์‹œ์„ฑ ์ฝ”๋“œ์˜ ๋ฐฉํ•ด ์—†์ด ์ˆœ์ฐจ์ ์œผ๋กœ(sequentially) ์‹คํ–‰๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์•„๋ž˜ ์ฝ”๋“œ๋Š” ํ•œ ๊ฐค๋Ÿฌ๋ฆฌ์—์„œ ๋‹ค๋ฅธ ๊ฐค๋Ÿฌ๋ฆฌ๋กœ ์‚ฌ์ง„์„ ์˜ฎ๊ธด๋‹ค.

let firstPhoto = await listPhotos(inGallery: "Summer Vacation")[0]
add(firstPhoto, toGallery: "Road Trip")
// At this point, firstPhoto is temporarily in both galleries.
remove(firstPhoto, fromGallery: "Summer Vacation")

add(_:toGallery:) ํ˜ธ์ถœ๊ณผ remove(_:fromGallery:) ํ˜ธ์ถœ ์‚ฌ์ด์— ๋‹ค๋ฅธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•  ๋ฐฉ๋ฒ•์ด ์—†๋‹ค. ๊ทธ ์‹œ๊ฐ„ ๋™์•ˆ, ์ฒซ ๋ฒˆ์งธ ์‚ฌ์ง„์ด ๋‘ ๊ฐค๋Ÿฌ๋ฆฌ ๋ชจ๋‘์— ๋‚˜ํƒ€๋‚˜ ์ผ์‹œ์ ์œผ๋กœ ์•ฑ์˜ ๋ถˆ๋ณ€์„ฑ(invariant) ์ค‘ ํ•˜๋‚˜๋ฅผ ์œ„๋ฐฐํ•œ๋‹ค. ๋” ์ด์ƒ์˜ await ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด์„  ์•ˆ ๋จ์„ ๋ช…ํ™•ํžˆ ํ•˜๊ธฐ ์œ„ํ•ด, ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๋™๊ธฐ ํ•จ์ˆ˜๋กœ ๋ฆฌํŒฉํ† ๋งํ•  ์ˆ˜ ์žˆ๋‹ค:

func move(_ photoName: String, from source: String, to destination: String) {
    add(photoName, toGallery: destination)
    remove(photoName, fromGallery: source)
}
// ...
let firstPhoto = await listPhotos(inGallery: "Summer Vacation")[0]
move(firstPhoto, from: "Summer Vacation", to: "Road Trip")

์œ„์˜ ์˜ˆ์‹œ์—์„œ, move(_:from:to:) ํ•จ์ˆ˜๊ฐ€ ๋™๊ธฐ ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋‹น์‹ ์€ ์–ด๋–ค ์ผ์‹œ์ •์ง€๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž„์„ ํ™•์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‚˜์ค‘์— ๋งŒ์•ฝ ์ด ํ•จ์ˆ˜์— ์ผ์‹œ์ •์ง€๋  ์ˆ˜ ์žˆ๋Š” ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๋Œ€์‹  ์ปดํŒŒ์ผ ํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

์ฐธ๊ณ 

Task.sleep(until:tolerance:clock:) ๋ฉ”์„œ๋“œ๋Š” ๋™์‹œ์„ฑ์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ๋ฐฐ์šฐ๊ธฐ ์œ„ํ•œ ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์œ ์šฉํ•˜๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ์•„๋ฌด ๊ฒƒ๋„ ํ•˜์ง€ ์•Š์ง€๋งŒ, ๋ฐ˜ํ™˜๋˜๊ธฐ ์ „์— ์ตœ์†Œํ•œ ์ฃผ์–ด์ง„ ์‹œ๊ฐ„๋งŒํผ ๊ธฐ๋‹ค๋ฆฐ๋‹ค. ์•„๋ž˜๋Š” ์œ„์˜ listPhotos(inGallery:) ํ•จ์ˆ˜์— sleep(until:tolerance:clock:) ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ์„ ํ‰๋‚ด๋‚ธ ๊ฒƒ์ด๋‹ค :

func listPhotos(inGallery name: String) async throws -> [String] {
    try await Task.sleep(until: .now + .seconds(2), clock: .continuous)
    return ["IMG001", "IMG99", "IMG0404"]
}

๋น„๋™๊ธฐ ์‹œํ€€์Šค

์ด์ „ ์„น์…˜์˜ listPhotos(inGallery:) ํ•จ์ˆ˜๋Š”, ๋ฐฐ์—ด์˜ ๋ชจ๋“  ์š”์†Œ๊ฐ€ ์ค€๋น„๋œ ์ดํ›„์— ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ „์ฒด ๋ฐฐ์—ด์„ ํ•œ ๋ฒˆ์— ๋ฐ˜ํ™˜ํ•œ๋‹ค.
๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ, ๋น„๋™๊ธฐ ์‹œํ€€์Šค(asynchronous sequence) ๋ฅผ ์‚ฌ์šฉํ•ด ํ•œ ๋ฒˆ์— ์ปฌ๋ ‰์…˜์˜ ํ•œ ์š”์†Œ๋ฅผ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜๋Š” ๋น„๋™๊ธฐ ์‹œํ€€์Šค๋ฅผ ๋ฐ˜๋ณต(iterating over)ํ•˜๋Š” ๋ชจ์Šต์ด๋‹ค:

import Foundation

let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
    print(line)
}

์œ„์˜ ์˜ˆ์‹œ๋Š” ์ผ๋ฐ˜์ ์ธ for-in ๋ฐ˜๋ณต๋ฌธ ๋Œ€์‹ ์—, for ๋ฅผ await ๊ณผ ํ•จ๊ป˜ ์ž‘์„ฑํ–ˆ๋‹ค. ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ๋˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ์™€ ๊ฐ™์ด, await ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์€ ์ผ์‹œ์ค‘๋‹จ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. for-await-in ๋ฐ˜๋ณต๋ฌธ์€ ๋‹ค์Œ ์š”์†Œ๊ฐ€ ์ค€๋น„๋˜๋Š” ๊ฒƒ์„ ๊ธฐ๋‹ค๋ฆด ๋•Œ, ๊ฐ ๋ฐ˜๋ณต์˜ ์‹œ์ž‘ ์‹œ์ ์˜ ์‹คํ–‰์„ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค.

Sequence ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•จ์œผ๋กœ์จ for-in ๋ฐ˜๋ณต๋ฌธ์—์„œ ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, AsyncSequence ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•จ์œผ๋กœ์จ for-await-in ๋ฐ˜๋ณต๋ฌธ์—์„œ ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ž…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ณ‘๋ ฌ๋กœ ๋น„๋™๊ธฐ ํ•จ์ˆ˜ ํ˜ธ์ถœํ•˜๊ธฐ

await ๋กœ ์„ ์–ธ๋œ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ํ•œ ๋ฒˆ์— ํ•œ ์กฐ๊ฐ์˜ ์ฝ”๋“œ๋งŒ ์‹คํ–‰ํ•œ๋‹ค. ๋น„๋™๊ธฐ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋™์•ˆ, ํ˜ธ์ถœ์ž๋Š” ๋‹ค์Œ ์ฝ”๋“œ ์ค„๋กœ ์ด๋™ํ•˜๊ธฐ ์ „์— ๊ทธ ์ฝ”๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐค๋Ÿฌ๋ฆฌ์—์„œ ์ฒ˜์Œ๋ถ€ํ„ฐ 3๊ฐœ์˜ ์‚ฌ์ง„์„ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด, ์•„๋ž˜์™€ ๊ฐ™์ด downloadPhoto(named:) ํ•จ์ˆ˜๋ฅผ await ๋กœ ์„ธ ๋ฒˆ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])

let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)

์ด ๋ฐฉ๋ฒ•์€ ์ค‘์š”ํ•œ ์•ฝ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค: ๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋น„๋™๊ธฐ์ด๊ณ  ๊ทธ ์ง„ํ–‰ ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋„๋ก ๋˜์–ด ์žˆ์ง€๋งŒ, ํ•œ ๋ฒˆ์— ์˜ค์ง ํ•˜๋‚˜์˜ downloadPhoto(named:) ๋งŒ์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ์‚ฌ์ง„์€ ๋‹ค์Œ ๊ฒƒ์˜ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์‹œ์ž‘๋˜๊ธฐ ์ „์— ์™„์ „ํžˆ ๋‹ค์šด๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋Ÿฌํ•œ ์ž‘์—…์„ ๊ธฐ๋‹ค๋ฆด ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค - ๊ฐ๊ฐ์˜ ์‚ฌ์ง„์€ ๋…๋ฆฝ์ ์œผ๋กœ ๋‹ค์šด๋กœ๋“œ๋  ์ˆ˜ ์žˆ๊ณ , ์‹ฌ์ง€์–ด ๋™์‹œ์—๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์ฃผ๋ณ€์˜ ์ฝ”๋“œ์™€ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋˜๋„๋ก ํ•˜๋ ค๋ฉด, ์ƒ์ˆ˜๋ฅผ ์ •์˜ํ•  ๋•Œ let ์•ž์— async ๋ฅผ ์ž‘์„ฑํ•œ ๋‹ค์Œ, ๊ทธ ์ƒ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค await ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])

let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)

์ด ์˜ˆ์‹œ์—์„œ๋Š”, 3๊ฐœ์˜ downloadPhoto(named:) ํ˜ธ์ถœ์ด ๋ชจ๋‘ ์•ž์˜ ์ž‘์—…์ด ๋๋‚˜๋Š” ๊ฒƒ์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์ถฉ๋ถ„ํ•œ ์‹œ์Šคํ…œ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์ด ์ž‘์—…๋“ค์€ ๋™์‹œ์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜ ํ˜ธ์ถœ ์ค‘ ์–ด๋–ค ๊ฒƒ๋„ await ๋กœ ํ‘œ์‹œ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด, ์ฝ”๋“œ๊ฐ€ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ธฐ ์œ„ํ•ด ์ผ์‹œ์ค‘๋‹จ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋Œ€์‹ , photos ๊ฐ€ ์ •์˜๋œ ์ค„๊นŒ์ง€ ์‹คํ–‰์ด ๊ณ„์†๋ฉ๋‹ˆ๋‹ค. ์ด ์ค„์—์„œ๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ๋น„๋™๊ธฐ ํ˜ธ์ถœ๋“ค์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•„์š”๋กœ ํ•˜๋ฏ€๋กœ, ์„ธ ๊ฐœ์˜ ์‚ฌ์ง„์ด ๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋‹ค ๋  ๋•Œ๊นŒ์ง€ ์‹คํ–‰์„ ์ผ์‹œ์ •์ง€ํ•˜๊ธฐ ์œ„ํ•ด await ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ๋ฒ•์˜ ์ฐจ์ด๋Š” ์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  • await ๋กœ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ๋Š”, ์ดํ›„์˜ ์ฝ”๋“œ๋“ค์ด ๊ทธ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ์— ์˜์กดํ•˜๊ณ  ์žˆ์„ ๋•Œ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ˆœ์ฐจ์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜๋Š” ์ž‘์—…์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • async-let ์œผ๋กœ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒฝ์šฐ๋Š”, ์ดํ›„ ์ฝ”๋“œ์—์„œ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š์„ ๋•Œ ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋˜๋Š” ์ž‘์—…์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  • await ์™€ async-let ์€ ๋ชจ๋‘ ์ž์‹ ์ด ์ผ์‹œ์ •์ง€๋˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ํ—ˆ๋ฝํ•ฉ๋‹ˆ๋‹ค.
  • ๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘, ์ผ์‹œ์ •์ง€๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์„ await ๋กœ ํ‘œ์‹œํ•จ์œผ๋กœ์จ ์‹คํ–‰์ด ์ค‘์ง€๋  ์ˆ˜ ์žˆ๊ณ , ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๊นŒ์ง€ ์ค‘์ง€๋  ์ˆ˜ ์žˆ์Œ์„ ๋‚˜ํƒ€๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹น์‹ ์€ ์ด ๋‘ ๋ฐฉ๋ฒ•์„ ๊ฐ™์€ ์ฝ”๋“œ์— ํ•จ๊ป˜ ์„ž์–ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

Tasks์™€ Task Groups

Task๋Š” ๋‹น์‹ ์˜ ํ”„๋กœ๊ทธ๋žจ์˜ ์ผ๋ถ€๋กœ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…๋‹จ์œ„๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๋น„๋™๊ธฐ ์ฝ”๋“œ๋Š” ์–ด๋–ค task์˜ ์ผ๋ถ€๋กœ์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
์ด์ „ ์„น์…˜์—์„œ ์„ค๋ช…ํ•œ async-let ๋ฌธ๋ฒ•์˜ ๊ฒฝ์šฐ, ๋‹น์‹ ์—๊ฒŒ child task๋ฅผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ๋˜ํ•œ task group์„ ๋งŒ๋“ค๊ณ  ๊ฑฐ๊ธฐ์— ์ž์‹task๋“ค์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์šฐ์„  ์ˆœ์œ„์™€ ์ทจ์†Œ์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์ œ์–ด๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋ฉฐ, ๋™์ ์ธ ์ˆซ์ž์˜ task๋“ค์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค๋‹ˆ๋‹ค.
task๋“ค์€ ๊ณ„์ธต์œผ๋กœ ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค. task group์˜ ๊ฐ๊ฐ์˜ task๋Š” ๊ฐ™์€ ๋ถ€๋ชจtask๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๊ฐ๊ฐ์˜ task๋Š” ์ž์‹task๋“ค์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. task๋“ค๊ณผ task group๋“ค ์‚ฌ์ด์˜ ๋ช…์‹œ์ ์ธ ๊ด€๊ณ„๋กœ ์ธํ•ด, ์ด๋Ÿฌํ•œ ์ ‘๊ทผ๋ฐฉ์‹์„ ๊ตฌ์กฐํ™”๋œ ๋™์‹œ์„ฑ(structured concurrency) ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ์ •ํ™•์„ฑ์— ๋Œ€ํ•œ ์•ฝ๊ฐ„์˜ ์ฑ…์ž„์„ ๊ฐ–๊ธด ํ•˜์ง€๋งŒ, task๋“ค ์‚ฌ์ด์˜ ๋ช…์‹œ์ ์ธ parent-child ๊ด€๊ณ„ ๋•๋ถ„์— Swift๊ฐ€ ์ทจ์†Œ ์ „ํŒŒ(propagating cancellation)์™€ ๊ฐ™์€ ๋ช‡๋ช‡ ๋™์ž‘์„ ๋‹น์‹ ์„ ์œ„ํ•ด ํ•ด์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ Swift๊ฐ€ ์ปดํŒŒ์ผ ํƒ€์ž„์— ๋ช‡๋ช‡ ์—๋Ÿฌ๋“ค์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

await withTaskGroup(of: Data.self) { taskGroup in
    let photoNames = await listPhotos(inGallery: "Summer Vacation")
    for name in photoNames {
        taskGroup.addTask { await downloadPhoto(named: name) }
    }
}

task group์— ๋Œ€ํ•œ ๋” ์ž์„ธํ•œ ์ •๋ณด๋Š” TaskGroup ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

๊ตฌ์กฐํ™”๋˜์ง€ ์•Š์€ ๋™์‹œ์„ฑ (Unstructured Concurrency)

์œ„ ์„น์…˜์— ์„ค๋ช…๋œ ๋™์‹œ์„ฑ์— ๋Œ€ํ•œ ๊ตฌ์กฐํ™”๋œ ์ ‘๊ทผ์— ๋”ํ•ด, Swift๋Š” ๊ตฌ์กฐํ™”๋˜์ง€ ์•Š์€ ๋™์‹œ์„ฑ ๋˜ํ•œ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. task group์— ์†ํ•œ task์™€ ๋‹ฌ๋ฆฌ, unstructured task ๋Š” ๋ถ€๋ชจ task๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ํ”„๋กœ๊ทธ๋žจ์— ํ•„์š”ํ•œ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ๋“  unstructured task๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์™„์ „ํ•œ ์œ ์—ฐ์„ฑ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ ์ •ํ™•์„ฑ์— ๋Œ€ํ•œ ์™„์ „ํ•œ ์ฑ…์ž„ ๋˜ํ•œ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
ํ˜„์žฌ actor์—์„œ ์‹คํ–‰๋˜๋Š” unstructured task๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š”, Task.init(priority:operation:) ์ด๋‹ˆ์…œ๋ผ์ด์ €๋ฅผ ํ˜ธ์ถœํ•˜์‹ญ์‹œ์˜ค. ํ˜„์žฌ actor์— ํฌํ•จ๋˜์ง€ ์•Š๋Š” unstructured task(๋ณดํ†ต detached task ๋ผ๊ณ  ํŠน์ •ํ•ด ๋ถ€๋ฆ…๋‹ˆ๋‹ค)๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š”, Task.detached(priority:operation:) ํด๋ž˜์Šค ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์‹ญ์‹œ์˜ค. ์ด ๋‘ ์ž‘์—…์€ ๋ชจ๋‘ ๋‹น์‹ ์ด ์ƒํ˜ธ์ž‘์šฉ(๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ฑฐ๋‚˜ ์ทจ์†Œํ•˜๋Š” ๋“ฑ)ํ•  ์ˆ˜ ์žˆ๋Š” task๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

let newPhoto = // ... some photo data ...
let handle = Task {
    return await add(newPhoto, toGalleryNamed: "Spring Adventures")
}
let result = await handle.value

detached task์˜ ๊ด€๋ฆฌ์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์ •๋ณด๋Š” Task ๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

Task ์ทจ์†Œ (Task Cancellation)

Swift ๋™์‹œ์„ฑ์€ ํ˜‘์กฐ์ ์ธ ์ทจ์†Œ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ task๋Š” ์‹คํ–‰์— ์žˆ์–ด ์ ์ ˆํ•œ ์‹œ์ ์—์„œ ์ทจ์†Œ๋˜์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ , ์ ์ ˆํ•œ ๋ฐฉ์‹์œผ๋กœ ์ทจ์†Œ์— ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ํ•˜๊ณ  ์žˆ๋Š” ์ž‘์—…์— ๋”ฐ๋ผ, ์ด๊ฒƒ์€ ๋ณดํ†ต ์•„๋ž˜์˜ ๊ฒƒ๋“ค ์ค‘ ํ•˜๋‚˜๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค:

  • CancellationError ์™€ ๊ฐ™์€ ์—๋Ÿฌ ๋˜์ง€๊ธฐ
  • nil ๋˜๋Š” ๋นˆ ์ปฌ๋ ‰์…˜ ๋ฐ˜ํ™˜ํ•˜๊ธฐ
  • ๋ถ€๋ถ„์ ์œผ๋กœ ์™„๋ฃŒ๋œ work ๋ฐ˜ํ™˜ํ•˜๊ธฐ

์ทจ์†Œ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด, task๊ฐ€ ์ทจ์†Œ๋˜์—ˆ์„ ๋•Œ CancellationError ๋ฅผ ๋˜์ง€๋Š” Task.checkCancellation() ์„ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜, ํ˜น์€ Task.isCancelled์˜ ๊ฐ’์„ ํ™•์ธํ•ด ๋‹น์‹ ์˜ ์ฝ”๋“œ์˜ ์ทจ์†Œ๋ฅผ ์ฒ˜๋ฆฌํ•˜์‹ญ์‹œ์˜ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐค๋Ÿฌ๋ฆฌ๋กœ๋ถ€ํ„ฐ ์‚ฌ์ง„์„ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ์ž‘์—…์€ ๋ถ€๋ถ„์ ์ธ ๋‹ค์šด๋กœ๋“œ๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ์ข…๋ฃŒํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์ทจ์†Œ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ „ํŒŒํ•˜๋ ค๋ฉด Task.cancel() ์„ ํ˜ธ์ถœํ•˜์‹ญ์‹œ์˜ค.

Actors

๋‹น์‹ ์˜ ํ”„๋กœ๊ทธ๋žจ์„ ๋…๋ฆฝ๋œ ๋™์‹œ์„ฑ ์กฐ๊ฐ์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ ์œ„ํ•ด task๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. task๋Š” ์„œ๋กœ๋กœ๋ถ€ํ„ฐ ๋…๋ฆฝ๋˜์–ด ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๋™์‹œ์— ์‹คํ–‰๋˜๋”๋ผ๋„ ์•ˆ์ „ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฐ€๋”์€ task๋“ค ์‚ฌ์ด์— ์–ด๋–ค ์ •๋ณด๋ฅผ ๊ณต์œ ํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. Actors๋Š” ๋‹น์‹ ์ด ๋™์‹œ์„ฑ ์ฝ”๋“œ ์‚ฌ์ด์— ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด ์ค๋‹ˆ๋‹ค.
ํด๋ž˜์Šค์™€ ๊ฐ™์ด, actors๋„ ์ฐธ์กฐ ํƒ€์ž…์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐ’ ํƒ€์ž…๊ณผ ์ฐธ์กฐ ํƒ€์ž…์„ ๋น„๊ตํ•˜๋Š” Classes Are Reference Types ์˜ ๋‚ด์šฉ์€ actor์—๋„ ํด๋ž˜์Šค์ฒ˜๋Ÿผ ๋™์ผํ•˜๊ฒŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
ํด๋ž˜์Šค์™€ ๋‹ค๋ฅด๊ฒŒ, actors๋Š” ํ•œ ๋ฒˆ์— ์˜ค์ง ํ•˜๋‚˜์˜ task๋งŒ์ด mutableํ•œ ์ƒํƒœ์— ์ ‘๊ทผํ•˜๋„๋ก ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‹ค์ˆ˜์˜ task๋“ค์ด actor์˜ ๊ฐ™์€ ์ธ์Šคํ„ด์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋” ์•ˆ์ „ํ•œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ์—ฌ๊ธฐ์— ์˜จ๋„๋ฅผ ๊ธฐ๋กํ•˜๋Š” actor๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

actor TemperatureLogger {
    let label: String
    var measurements: [Int]
    private(set) var max: Int // ์™ธ๋ถ€์—์„œ๋Š” ์ฝ๊ธฐ๋งŒ, ๋‚ด๋ถ€์—์„œ๋Š” ์“ฐ๊ธฐ๋„ ๊ฐ€๋Šฅ

    init(label: String, measurement: Int) {
        self.label = label
        self.measurements = [measurement]
        self.max = measurement
    }
}

actor๋Š” actor ํ‚ค์›Œ๋“œ๋กœ ์†Œ๊ฐœํ•˜๋ฉฐ, ๊ทธ ์ •์˜๋ฅผ ์ค‘๊ด„ํ˜ธ ๋‚ด์— ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. TemperatureLogger actor๋Š” actor ๋ฐ”๊นฅ์˜ ๋‹ค๋ฅธ ์ฝ”๋“œ๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ๊ฐ€์ง€๋ฉฐ, max ํ”„๋กœํผํ‹ฐ๋Š” ์˜ค์ง actor ๋‚ด๋ถ€์˜ ์ฝ”๋“œ๋งŒ ์ตœ๋Œ€๊ฐ’์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋„๋ก ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.
actor์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋„ ๊ตฌ์กฐ์ฒด์™€ ํด๋ž˜์Šค์™€ ๊ฐ™์€ ์ด๋‹ˆ์…œ๋ผ์ด์ € ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. actor์˜ ํ”„๋กœํผํ‹ฐ๋‚˜ ๋ฉ”์„œ๋“œ์— ์ ‘๊ทผํ•  ๋•Œ, await ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ผ์‹œ์ •์ง€๊ฐ€ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
// Prints "25"

์ด ์˜ˆ์‹œ์—์„œ, logger.max ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์€ ์ผ์‹œ์ •์ง€๊ฐ€ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด actor๋Š” ์ž์‹ ์˜ mutableํ•œ ์ƒํƒœ์— ํ•œ ๋ฒˆ์— ์˜ค์ง ํ•˜๋‚˜์˜ task๊ฐ€ ์ ‘๊ทผํ•˜๋„๋ก ์ œํ•œํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‹ค๋ฅธ task๊ฐ€ ์ด๋ฏธ logger์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ์ด ์ฝ”๋“œ๋Š” ๊ทธ ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋‹ค๋ฆฌ๋Š๋ผ ์ฝ”๋“œ๊ฐ€ ์ผ์‹œ์ •์ง€ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋ฐ˜๋ฉด, actor์˜ ์ผ๋ถ€์ธ ์ฝ”๋“œ๋Š” actor์˜ ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•  ๋•Œ await ํ‚ค์›Œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์—ฌ๊ธฐ์— TemperatureLogger ๋ฅผ ์ƒˆ๋กœ์šด ์˜จ๋„๋กœ ๊ฐฑ์‹ ํ•˜๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

extension TemperatureLogger {
    func update(with measurement: Int) {
        measurements.append(measurement)
        if measurement > max {
            max = measurement
        }
    }
}

update(with:) ๋ฉ”์„œ๋“œ๋Š” ์ด๋ฏธ actor ๋‚ด์—์„œ ์‹คํ–‰๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ทธ๊ฒƒ์€ max์™€ ๊ฐ™์€ ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•  ๋•Œ await ๋ฅผ ๋ถ™์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ๋˜ํ•œ ์™œ actor๊ฐ€ ์ž์‹ ์˜ mutableํ•œ ์ƒํƒœ์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ๋•Œ ํ•œ ๋ฒˆ์— ์˜ค์ง ํ•˜๋‚˜์˜ task๋งŒ์„ ํ—ˆ์šฉํ•˜๋Š”์ง€ ๊ทธ ์ด์œ ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค: actor์˜ ์ƒํƒœ์— ๋Œ€ํ•œ ์–ด๋–ค ๋ณ€๊ฒฝ์€ ์ผ์‹œ์ ์œผ๋กœ ๋ถˆ๋ณ€์„ฑ(invariants)์„ ๊นจ๋œจ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. TemperatureLogger actor๋Š” ์˜จ๋„์˜ ๋ฆฌ์ŠคํŠธ์™€ ์ตœ๊ณ  ์˜จ๋„๋ฅผ ๊ณ„์† ์ถ”์ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ƒˆ๋กœ์šด ์˜จ๋„๊ฐ€ ๊ธฐ๋ก๋˜์—ˆ์„ ๋•Œ ์ตœ๊ณ  ์˜จ๋„๋ฅผ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐฑ์‹  ์ค‘๊ฐ„, ์ฆ‰ ์ƒˆ ์ธก์ •๊ฐ’์„ ๋ถ™์ธ ํ›„์™€ max ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ธฐ ์ „ ์‚ฌ์ด์— TemperatureLogger ๋Š” ์ผ์‹œ์ ์œผ๋กœ ์ผ๊ด€์„ฑ์ด ์—†๋Š” ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๋™์‹œ์— ๊ฐ™์€ ์ธ์Šคํ„ด์Šค์— ์—ฌ๋Ÿฌ task๋“ค์ด ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์Œ์œผ๋กœ์จ, ์•„๋ž˜์™€ ๊ฐ™์€ ์—ฐ์†๋œ ์‚ฌ๊ฑด ๋ฐœ์ƒ์œผ๋กœ ์ธํ•œ ๋ฌธ์ œ๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ๋‹น์‹ ์˜ ์ฝ”๋“œ๊ฐ€ update(with:) ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ €, ๊ทธ๊ฒƒ์€ measurement ๋ฐฐ์—ด์˜ ๊ฐ’์„ ๋ฐ”๊พธ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  2. ๋‹น์‹ ์˜ ์ฝ”๋“œ๊ฐ€ max ๊ฐ’์„ ๋ฐ”๊พธ๊ธฐ ์ „์—, ๋‹ค๋ฅธ ๊ณณ์˜ ์ฝ”๋“œ๊ฐ€ ์˜จ๋„ ๋ฐฐ์—ด๊ณผ ์ตœ๋Œ€๊ฐ’์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
  3. ๋‹น์‹ ์˜ ์ฝ”๋“œ๊ฐ€ max ๊ฐ’์˜ ๊ฐฑ์‹ ์„ ๋๋ƒ…๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์€ ๊ฒฝ์šฐ, ๋‹ค๋ฅธ ๊ณณ์—์„œ ์‹คํ–‰์ค‘์ธ ์ฝ”๋“œ๋Š” ๋ถ€์ •ํ™•ํ•œ ์ •๋ณด๋ฅผ ์ฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด actor์— ๋Œ€ํ•œ ์ ‘๊ทผ์ด ๋ฐ์ดํ„ฐ๊ฐ€ ์ผ์‹œ์ ์œผ๋กœ ์œ ํšจํ•˜์ง€ ์•Š์€ ์‹œ์ ์— update(with:) ํ˜ธ์ถœ ๋„์ค‘์— interleave(๊ต์ฐจ๋˜์–ด) ์ผ์–ด๋‚ฌ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. Swift actor๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ด๋Ÿฐ ๋ฌธ์ œ๋Š” ์˜ˆ๋ฐฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํ•œ ๋ฒˆ์— ์ƒํƒœ๊ฐ’์— ๋Œ€ํ•œ ํ•˜๋‚˜์˜ ์ž‘์—…๋งŒ์„ ํ—ˆ์šฉํ•˜๊ณ , await๊ฐ€ ์ผ์‹œ์ค‘๋‹จ ์ง€์ ์„ ํ‘œ์‹œํ•œ ์ง€์ ์—์„œ๋งŒ ์ฝ”๋“œ์˜ ์ค‘๋‹จ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. update(with:) ๋Š” ์–ด๋–ค ์ผ์‹œ์ค‘๋‹จ ์ง€์ ๋„ ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์–ด๋–ค ๋‹ค๋ฅธ ์ฝ”๋“œ๋„ ์—…๋ฐ์ดํŠธ ์ค‘๊ฐ„์— ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

interleave : ์–ด๋–ค ํ”„๋กœ๊ทธ๋žจ์˜ ์ผ๋ถ€๋ฅผ ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ์— ๋ผ์›Œ ๋„ฃ๋Š” ์ผ. ๊ต์ฐจ๋กœ ๋ฐฐ์น˜ํ•˜๋Š” ์ผ?

๋งŒ์•ฝ ๋‹น์‹ ์ด actor ๋ฐ”๊นฅ์—์„œ ์ด๋Ÿฌํ•œ ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•˜๋ ค ํ•œ๋‹ค๋ฉด(๋งˆ์น˜ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•˜๋“ฏ์ด), ๋‹น์‹ ์€ ์ปดํŒŒ์ผ ํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋งŒ๋‚˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด :

print(logger.max)  // Error

await ํ‚ค์›Œ๋“œ ์ž‘์„ฑ ์—†์ด logger.max ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์€ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด actor์˜ ํ”„๋กœํผํ‹ฐ๋“ค์€ actor์˜ ๋…๋ฆฝ๋œ local state์˜ ์ผ๋ถ€์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. Swift๋Š” ์˜ค์ง actor ๋‚ด๋ถ€์˜ ์ฝ”๋“œ๋งŒ actor์˜ local state์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ณด์žฅ์€ actor ๊ฒฉ๋ฆฌ(actor isolation) ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Sendable Types

task์™€ actor๋Š” ๋‹น์‹ ์˜ ํ”„๋กœ๊ทธ๋žจ์ด ์•ˆ์ „ํ•˜๊ฒŒ ๋™์‹œ์ ์œผ๋กœ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ๋‚˜๋ˆ„๋Š” ๊ฒƒ์„ ๋•์Šต๋‹ˆ๋‹ค. task๋‚˜ actor์˜ ์ธ์Šคํ„ด์Šค ๋‚ด๋ถ€์—์„œ, mutableํ•œ ์ƒํƒœ(ํ”„๋กœํผํ‹ฐ๋‚˜ ๋ณ€์ˆ˜ ๊ฐ™์€)๋ฅผ ํฌํ•จํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ๋ถ€๋ถ„์€ ๋™์‹œ์„ฑ ๋„๋ฉ”์ธ(concurrency domain) ์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ์–ด๋–ค ์ข…๋ฅ˜์˜ ๋ฐ์ดํ„ฐ๋Š” ๋™์‹œ์„ฑ ๋„๋ฉ”์ธ ๊ฐ„์— ๊ณต์œ ๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ mutableํ•œ ์ƒํƒœ๋ฅผ ํฌํ•จํ•˜์ง€๋งŒ ์ค‘๋ณต ์ ‘๊ทผ(overlapping access)์œผ๋กœ๋ถ€ํ„ฐ ๋ณดํ˜ธํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
ํ•˜๋‚˜์˜ ๋™์‹œ์„ฑ ๋„๋ฉ”์ธ์œผ๋กœ๋ถ€ํ„ฐ ๋‹ค๋ฅธ ๋„๋ฉ”์ธ์œผ๋กœ ๊ณต์œ ๋  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์€ sendable type ์ด๋ผ๊ณ  ์•Œ๋ ค์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ทธ๊ฒƒ์€ actor ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ task์˜ ๊ฒฐ๊ณผ๋กœ์„œ ๋ฐ˜ํ™˜๋  ๋•Œ ์•„๊ทœ๋จผํŠธ์ฒ˜๋Ÿผ ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฑ•ํ„ฐ ์ด์ „์˜ ์˜ˆ์‹œ๋“ค์€ sendability์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๊ทธ ์˜ˆ์‹œ๋“ค์ด ๋™์‹œ์„ฑ ๋„๋ฉ”์ธ ๊ฐ„์— ์ „๋‹ฌ ๋ฐ ๊ณต์œ ๋˜์–ด๋„ ์•ˆ์ „ํ•œ ๋ฐ์ดํ„ฐ์ธ ๊ฐ„๋‹จํ•œ ๊ฐ’ ํƒ€์ž…๋งŒ์„ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ, ์–ด๋–ค ํƒ€์ž…๋“ค์€ ๋™์‹œ์„ฑ ๋„๋ฉ”์ธ ์‚ฌ์ด์— ์ „๋‹ฌ๋˜๊ธฐ์— ์•ˆ์ „ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, mutableํ•œ ํ”„๋กœํผํ‹ฐ๋ฅผ ํฌํ•จํ•˜๋ฉฐ ๊ทธ ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ง๋ ฌํ™”(serialize)ํ•˜์ง€ ์•Š๋Š” ํด๋ž˜์Šค๋Š”, ๋‹น์‹ ์ด ๊ทธ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์„œ๋กœ ๋‹ค๋ฅธ task๊ฐ„์— ์ „๋‹ฌํ•  ๋•Œ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๊ณ  ๋ถ€์ •ํ™•ํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋‚ณ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
sendableํ•œ ํƒ€์ž…์œผ๋กœ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Sendable ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•ด ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ์ด ํ”„๋กœํ† ์ฝœ์€ ์–ด๋–ค ์ฝ”๋“œ๋„ ์š”๊ตฌํ•˜์ง€ ์•Š์ง€๋งŒ, Swift๊ฐ€ ๊ฐ•์ œํ•˜๋Š” ์˜๋ฏธ์  ์š”๊ตฌ์‚ฌํ•ญ(semantic requirements) ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ, sendableํ•œ ํƒ€์ž…์ด ๋˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์„ธ ๊ฐ€์ง€๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค:

  • ํƒ€์ž…์ด ๊ฐ’ ํƒ€์ž…์ด๋ฉฐ, ๊ทธ๊ฒƒ์˜ mutableํ•œ ์ƒํƒœ๊ฐ€ ๋‹ค๋ฅธ sendableํ•œ ํ…Œ์ดํ„ฐ๋กœ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์ง - ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ตฌ์กฐ์ฒด๊ฐ€ ์ €์žฅ ํ”„๋กœํผํ‹ฐ๋“ค๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š”๋ฐ, ๊ทธ ์ €์žฅํ”„๋กœํผํ‹ฐ๋“ค์ด sendableํ•œ ๊ฒฝ์šฐ. ๋˜๋Š” ์—ด๊ฑฐํ˜•์˜ ์—ฐ๊ด€๊ฐ’๋“ค์ด sendableํ•œ ๊ฒฝ์šฐ.
  • ํƒ€์ž…์ด ์–ด๋–ค mutableํ•œ ์ƒํƒœ๋„ ๊ฐ–์ง€ ์•Š์œผ๋ฉฐ, ๊ทธ๊ฒƒ์˜ immutableํ•œ ์ƒํƒœ๊ฐ€ ๋‹ค๋ฅธ sendableํ•œ ๊ฐ’์œผ๋กœ ๊ตฌ์„ฑ๋œ ๊ฒฝ์šฐ - ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ตฌ์กฐ์ฒด๋‚˜ ํด๋ž˜์Šค๊ฐ€ ์˜ค์ง ์ฝ๊ธฐ ์ „์šฉ(read-only) ํ”„๋กœํผํ‹ฐ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ.
  • ํƒ€์ž…์ด ๊ทธ๊ฒƒ์˜ mutableํ•œ ์ƒํƒœ์˜ ์•ˆ์ „์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง„ ๊ฒฝ์šฐ. ์˜ˆ๋ฅผ ๋“ค์–ด @MainActor ๋กœ ํ‘œ์‹œ๋œ ํด๋ž˜์Šค๋‚˜, ํŠน์ • ์Šค๋ ˆ๋“œ๋‚˜ ํ์˜ ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•œ ์ ‘๊ทผ์„ ์ง๋ ฌํ™”ํ•˜๋Š” ํด๋ž˜์Šค

์˜๋ฏธ์  ์š”๊ตฌ์‚ฌํ•ญ์— ๋Œ€ํ•œ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ Sendable ํ”„๋กœํ† ์ฝœ์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์–ด๋–ค ํƒ€์ž…์€ ํ•ญ์ƒ sendableํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด sendableํ•œ ํ”„๋กœํผํ‹ฐ๋งŒ ๊ฐ–๋Š” ๊ตฌ์กฐ์ฒด๋‚˜, sendableํ•œ ์—ฐ๊ด€๊ฐ’๋งŒ ๊ฐ–๋Š” ์—ด๊ฑฐํ˜•์˜ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด:

struct TemperatureReading: Sendable {
    var measurement: Int
}

extension TemperatureLogger {
    func addReading(from reading: TemperatureReading) {
        measurements.append(reading.measurement)
    }
}

let logger = TemperatureLogger(label: "Tea kettle", measurement: 85)
let reading = TemperatureReading(measurement: 45)
await logger.addReading(from: reading)

TemperatureReading ์€ sendableํ•œ ํ”„๋กœํผํ‹ฐ๋งŒ ๊ฐ–๋Š” ๊ตฌ์กฐ์ฒด์ด๊ณ , public ์ด๋‚˜ @usableFromInline์œผ๋กœ ํ‘œ์‹œ๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ด๊ฒƒ์€ ์•”๋ฌต์ ์œผ๋กœ sendableํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์•”๋ฌต์ ์œผ๋กœ Sendable ํ”„๋กœํ† ์ฝœ ์ฑ„ํƒ์ด ์ค€์ˆ˜๋œ ๊ตฌ์กฐ์ฒด์˜ ๋ฒ„์ „์ž…๋‹ˆ๋‹ค:

struct TemperatureReading {
    var measurement: Int
}