Skip to content

Latest commit

ย 

History

History
264 lines (186 loc) ยท 17 KB

TIL_2022:10:11_concurrency_swift5.5_basic.md

File metadata and controls

264 lines (186 loc) ยท 17 KB

Today I Learned


2022.10.11 (ํ™”)

ps) ์ด ๊ธ€์„ ์ž‘์„ฑํ•˜๋‹ค๊ฐ€ swift-book์—์„œ swift ๋ฌธ๋ฒ• ์˜ค๋ฅ˜ ๋ฐœ๊ฒฌํ•ด์„œ PR๋‚ ๋ฆผ. ๐Ÿ˜… ๐Ÿ˜†

Concurrency - Swift5.5 ์ด์ „

Swfit์–ธ์–ด๋Š” ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ์™€ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ์ž‘์—…์„ ๋„์™€์ฃผ๋„๋ก ์„ค๊ฒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด์˜ Swift์–ธ์–ด์—์„œ๋Š” ๋น„๋™๊ธฐ์ž‘์—…์„ completion handler๋ฅผ ์ด์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Š” ๋‹จ์ ์ด ๋ช‡๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, ์ด๋ฏธ์ง€์ด๋ฆ„ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์ฒซ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋กœ๋“œํ•œ ํ›„, ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ์ƒํ™ฉ์ด๋ผ๋ฉด,

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

์ด๋ฏธ ์ค‘์ฒฉ ํด๋กœ์ €๋ฅผ ํ†ตํ•ด ์ฝ๊ธฐ ์–ด๋ ค์šด ์ฝ”๋“œ๊ฐ€ ๋ ๋ฟ๋”๋Ÿฌ, ๋น„๋™๊ธฐ ์ž‘์—…์ด ๋” ์ถ”๊ฐ€๋œ๋‹ค๋ฉด ๋” ์ค‘์ฒฉ๋˜์–ด ๋ณต์žกํ•ด์งˆ ๊ฒ๋‹ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ์ž‘์—…์ด ์–ด๋–ค ์Šค๋ ˆ๋“œ์—์„œ ์ž‘์—…๋  ์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— UI์™€ ๊ด€๋ จ๋œ ์ž‘์—…์€ DispatchQueue.main์œผ๋กœ ๊ฐ์‹ธ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

ํ•œ๊ฐ€์ง€ ๋”, ํ•ด๋‹น ์ž‘์—…์ด ๋‘๋ฒˆ ์“ฐ์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•จ์ˆ˜ํ™”์‹œ์ผœ์•ผ ํ•  ๊ฒฝ์šฐ, ๋ชจ๋“  ์ƒํ™ฉ(์—๋Ÿฌ๊ฐ€ ๋‚œ ๊ฒฝ์šฐ)์—์„œ completion handler๋ฅผ ๋ฐ˜ํ™˜ํ–ˆ๋Š”์ง€ ๊ณ ๋ คํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์—๋Ÿฌ์ฒ˜๋ฆฌ๋ฅผ ๊ณ ๋ คํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋งŒ์ผ ๊ณ ๋ คํ•œ๋‹ค๋ฉด listPhotos, downloadPhoto์˜ completion handler๊ฐ€ Resultํƒ€์ž…์œผ๋กœ ๋ฆฌํ„ด๋˜๊ฒŒ ์ˆ˜์ •ํ•˜๊ณ  ํ•ด๋‹น ์˜ค๋ฅ˜์— ๋งž๊ฒŒ showPhoto์˜ completion handler๋ฅผ ๋ฐ˜ํ™˜ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๊ฐœ๋ฐœ์ž์˜ ์‹ค์ˆ˜๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋‹ค์ˆ˜ ์ƒ๊น๋‹ˆ๋‹ค.

func showPhoto(completion: @escpaing ((UIImage) -> Void)) {
    listPhotos(inGallery: "Summer Vacation") { photoNames in
        let sortedNames = photoNames.sorted()
        let name = sortedNames[0]
        downloadPhoto(named: name) { photo in                        
						completion(photo) // โœ…
        }
    }
}

๋งˆ์ง€๋ง‰์œผ๋กœ, retain cycle์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋กœ์ € ๋‚ด๋ถ€์— self ํ”„๋กœํผํ‹ฐ๋ฅผ ์ด์šฉํ•ด์„œ ์ ‘๊ทผํ•  ๋•Œ ์–ด๋Š ์“ฐ๋ ˆ๋“œ์—์„œ ์ ‘๊ทผํ•˜๊ฒŒ ๋  ๊ฒƒ์ธ์ง€ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. escaping closure์˜ ๊ฒฝ์šฐ ์ผ๋ฐ˜ ํด๋กœ์ €์™€๋Š” ๋‹ค๋ฅด๊ฒŒ, ๋ ˆํผ๋Ÿฐ์Šค ์นด์šดํŒ…์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. [weak self]๋ฅผ ํ†ตํ•ด ๋ ˆํผ๋Ÿฐ์Šค ์นด์šดํŒ…์„ ์ œ๊ฑฐํ•˜์—ฌ retain cycle ๋ฐœ์ƒ์„ ์˜ˆ๋ฐฉํ•ด์•ผ ํ•˜์ง€๋งŒ, ๋ฌด๋ถ„๋ณ„ํ•œ [weak self]์˜ ์‚ฌ์šฉ์€ ๋Ÿฐํƒ€์ž„ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ผ์œผํ‚ค๊ณ , ์˜ต์…”๋„์„ ์ฒดํฌํ•ด์ค˜์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์ƒ๊น๋‹ˆ๋‹ค.

listPhotos(inGallery: "Summer Vacation") { [weak self] photoNames in // โœ…
    let sortedNames = self?.photoNames.sorted() ?? [defaultImageName] // โœ…
    let name = sortedNames[0]
    downloadPhoto(named: name) { [weak self] photo in // โœ…
				DispatchQueme.main { [weak self] // โœ…
		        self?.show(photo) // โœ…     
        }
    }
}

completion handler์˜ ๋‹จ์ ์„ ์ •๋ฆฌํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ค‘์ฒฉํด๋กœ์ €๋กœ ์ธํ•ด ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ ์ €ํ•˜
  • ๊ฐœ๋ฐœ์ž์˜ ์‹ค์ˆ˜๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์ˆ˜์˜ ์˜ค๋ฅ˜ ์ƒํ™ฉ ์กด์žฌ
    • self capture -> retain cycle
    • ์—๋Ÿฌ ํ•ธ๋“ค๋ง

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

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

๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ ๋’ค์— async ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•  ๊ฒฝ์šฐ์—” ํƒ€์ž…์„ ํ™”์‚ดํ‘œ(->) ๋’ค์— ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์˜ค๋ฅ˜๋ฅผ ๋˜์งˆ ์ˆ˜ ์žˆ๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜์ผ ๊ฒฝ์šฐ์—” async throws๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

ํ•จ์ˆ˜์˜ ์ž‘๋™์„ ์ค‘๊ฐ„์— ๋ฉˆ์ถ”๋Š” ํ‚ค์›Œ๋“œ๋Š” await์ž…๋‹ˆ๋‹ค.

let photoNames = await listPhotos(inGallery: "Summer Vacation") // โ›”๏ธ
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name) // โ›”๏ธ
show(photo)

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

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

  • ๋น„๋™๊ธฐ ๋ฉ”์†Œ๋“œ ๋ฐ ํ”„๋กœํผํ‹ฐ ๋‚ด๋ถ€
  • @mainํ‚ค์›Œ๋“œ๋กœ ํ‘œ์‹œ๋œ structure, main, enum
  • Unstructured child task ๋‚ด๋ถ€ (์•„๋ž˜์—์„œ ์„ค๋ช…)

๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ (Asynchronous Sequeunce)

listPhotos(inGallery:) ํ•จ์ˆ˜๋Š” ๋ชจ๋“  ์ด๋ฏธ์ง€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ๋‹ค์Œ์— ๋‹ค์Œ ์ž‘์—…์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. Swift์˜ ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ์„ ์ด์šฉํ•˜๋ฉด ์ด๋ฏธ์ง€ ๋ฆฌ์ŠคํŠธ์™€ ๊ฐ™์ด collectionํƒ€์ž…์ธ ๊ฒฝ์šฐ ํ•˜๋‚˜์˜ element๋งŒ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ๊นŒ์ง€๋งŒ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import Foundation

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

for - await - in ๊ตฌ๋ฌธ์„ ํ†ตํ•ด ๊ฐ iteration๋งˆ๋‹ค ๊ฐ’์ด ์ค€๋น„๋ ๋•Œ๊นŒ์ง€ ๋ฉˆ์ถ”๊ณ  ๊ฐ’์ด ์ค€๋น„๋˜๋ฉด ๋‹ค์‹œ ๋™์ž‘ํ•˜๊ฒŒ๋”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ iteration์ด 10๋ฒˆ์ด๋ผ๋ฉด ์ด 10๋ฒˆ ๋ฉˆ์ถœ ๊ฒƒ์ž…๋‹ˆ๋‹ค. AsyncSequenceํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•œ ํƒ€์ž…์ด๋ผ๋ฉด for-await-in๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

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

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)

์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ์ฒซ๋ฒˆ์งธ ์ด๋ฏธ์ง€๊ฐ€ ๋‹ค์šด๋กœ๋“œ๋œ ํ›„, ๋‘๋ฒˆ์งธ ์ด๋ฏธ์ง€๊ฐ€ ๋‹ค์šด๋กœ๋“œ๋˜๊ณ , ๋‘๋ฒˆ์งธ ์ด๋ฏธ์ง€๊ฐ€ ๋‹ค์šด๋กœ๋“œ๋œ ํ›„ ์„ธ๋ฒˆ์งธ ์ด๋ฏธ์ง€๊ฐ€ ๋‹ค์šด๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋‘ ์ˆœ์ฐจ์ ์œผ๋กœ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ์ง„ํ–‰๋˜์ง€๋งŒ, ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ๋Š” ๋ชจ๋‘ ๋…๋ฆฝ์ ์œผ๋กœ, ์ฆ‰ ๋ณ‘๋ ฌ์ ์œผ๋กœ ์ง„ํ–‰๋˜์–ด์•ผํ•˜๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ๋ณ‘๋ ฌ์ ์œผ๋กœ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” asyncํ‚ค์›Œ๋“œ๋ฅผ letํ‚ค์›Œ๋“œ ์•ž์— ๋ถ™์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น ์ƒ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ 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๊ฐœ ๋‹ค์šด๋กœ๋“œ ์ž‘์—…์ด ๋™์‹œ์— ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค. ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๋น„๋™๊ธฐํ•จ์ˆ˜๋กœ ์ธํ–‰ ์‹คํ–‰์ด ๋ฉˆ์ถ”์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— awaitํ‚ค์›Œ๋“œ๋กœ ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹ ์—, photos ์ƒ์ˆ˜๊ฐ€ ํ• ๋‹น๋  ๋•Œ, ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  3๊ฐœ์˜ ๋น„๋™๊ธฐ ํ•จ์ˆ˜๊ฐ€ ๋ชจ๋‘ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ caller์˜ ์‹คํ–‰์ด ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค.

await๊ณผ async-let ์ •๋ฆฌ

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

Task and Task Groups

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

Task๋Š” ๊ณ„์ธต์ (hierarchy)์œผ๋กœ ์ •๋ฆฌ๋ฉ๋‹ˆ๋‹ค. Task Group๋‚ด์˜ ๊ฐ Task๋Š” ๊ฐ™์€ ๋ถ€๋ชจ Task๋ฅผ ๊ฐ€์ง€๊ณ , ๊ฐ Task๋Š” ์ž์‹ Task๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Task์™€ Task Group์ด๋ผ๋Š” ๋ช…์‹œ์ ์ธ ๊ด€๊ณ„๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ structured concurrency๋ผ๊ณ ๋„ ๋ถ€๋ฆ…๋‹ˆ๋‹ค. Task์™€ Task Group์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์„œ, ๊ฐœ๋ฐœ์ž์˜ ์‹ค์ˆ˜๊ฐ€ ์กฐ๊ธˆ ์ƒ๊ธธ์ง€๋ผ๋„, ๋ช…์‹œ์ ์ธ ๊ด€๊ณ„๋ฅผ ํ†ตํ•ด Swift๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„์— ์˜ค๋ฅ˜๋ฅผ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ๊ณ , ๋ถ€๋ชจ Cancellation -> ์ž์‹ Cancellation์™€ ๊ฐ™์€ ์ž‘์—…๋„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

Unstructured Concurrency

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

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

Spring Adeventures๋ผ๋Š” ๊ฐค๋Ÿฌ๋ฆฌ์— ์ƒˆ๋กœ์šด ์‚ฌ์ง„์„ ์ถ”๊ฐ€ํ•˜๋Š” Task๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค. Task์— ๋Œ€ํ•ด์„  ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด์„œ ๋” ์ž์„ธํžˆ ๊ณต๋ถ€ํ•ด์•ผ๊ฒ ์Šต๋‹ˆ๋‹ค.

Task Cancellation

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

  • CancellationError์™€ ๊ฐ™์€ ์—๋Ÿฌ๋ฅผ ๋˜์งˆ ๋•Œ
  • nil์ด๋‚˜ ๋นˆ collection์„ ๋ฐ˜ํ™˜ํ•  ๋•Œ
  • ๋ถ€๋ถ„์ ์œผ๋กœ ์™„๋ฃŒ๋œ ์ž‘์—…์„ ๋ฐ˜ํ™˜ํ•  ๋•Œ

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

Actors

์ด์ œ ๊ฐœ๋ฐœ์ž๋“ค์€ ํ”„๋กœ๊ทธ๋žจ์„ ๋™์‹œ์— ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋Š” Task ์กฐ๊ฐ์œผ๋กœ ์ชผ๊ฐค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด Task ์กฐ๊ฐ๋“ค์€ ๊ฐ๊ฐ ๋…๋ฆฝ๋˜์–ด ๋™์‹œ์— ์ž‘๋™ํ•˜์—ฌ ์„œ๋กœ๋กœ๋ถ€ํ„ฐ ์•ˆ์ „ํ•˜์ง€๋งŒ, Task ์‚ฌ์ด์—์„œ ๊ณต์œ ๋˜๋Š” ์ž์›๋“ค์€ ์•ˆ์ „ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Actor๋Š” ๋™์‹œ์„ฑ ์ฝ”๋“œ๋กœ๋ถ€ํ„ฐ ๊ณต์œ  ์ž์›์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.

Actor๋Š” ๋ ˆํผ๋Ÿฐ์Šค ํƒ€์ž…์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํด๋ž˜์Šค์™€ ๋‹ฌ๋ฆฌ, ํ•œ๋ฒˆ์— ํ•˜๋‚˜์˜ Task๋งŒ์ด State์— ์ ‘๊ทผํ•˜์—ฌ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์–ด ์—ฌ๋Ÿฌ๊ฐœ์˜ 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
    }
}

TemperatureLogger actor๋Š” ์™ธ๋ถ€์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ํ”„๋กœํผํ‹ฐ label๊ณผ measurements๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  max์™€ ๊ฐ™์ด actor ๋‚ด๋ถ€์—์„œ๋งŒ ์ ‘๊ทผํ•˜์—ฌ ์ˆ˜์ •๊ฐ€๋Šฅํ•œ ํ”„๋กœํผํ‹ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ œ class์™€ structure๊ณผ ๊ฐ™์€ initializer๋ฅผ ์ด์šฉํ•ด actor ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. Actor์˜ ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์†Œ๋“œ์— ์ ‘๊ทผํ•  ๋•, await ํ‚ค์›Œ๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ •์ง€ํ•  ํฌ์ธํŠธ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

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

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

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

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

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

๋งŒ์•ฝ actor์™ธ๋ถ€์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•œ๋‹ค๋ฉด, ์ปดํŒŒ์ผ ํƒ€์ž„ ์—๋Ÿฌ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.

print(logger.max)  // Error โ˜ ๏ธ

Swift๋Š” Actor๋‚ด๋ถ€์˜ ์ฝ”๋“œ๋งŒ์ด Actor์˜ ์ง€์—ญ๋ณ€์ˆ˜์— ๋ฐ”๋กœ ์ ‘๊ทผ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ actor isolation์ด๋ผ๊ณ ๋„ ํ•ฉ๋‹ˆ๋‹ค. (์šฉ์–ด๊ฐ€ ์ค‘์š”ํ•œ๊ฐ€?๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋‚˜์ค‘์— ์–ธ์ œ ๋‚˜์˜ฌ์ง€ ๋ชจ๋ฅด๋‹ˆ ์•Œ์•„๋Š” ๋‘์ž. ๊นŒ๋จน์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ข…์ข… ์ฝ์–ด๋ณด์ž)

actor isoliation: swift๊ฐ€ actor์™ธ๋ถ€์—์„œ actor ๋‚ด๋ถ€ ์ง€์—ญ๋ณ€์ˆ˜์— ๋ฐ”๋กœ ์ ‘๊ทผ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๊ณ  ๋‚ด๋ถ€์—์„œ๋งŒ ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•œ ๊ฒƒ์„ ๋ณด์žฅํ•˜๋Š” ์ƒํ™ฉ (์™ธ๋ถ€์—์„œ ์ ‘๊ทผํ•˜๊ธฐ ์œ„ํ•ด์„  awaitํ‚ค์›Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.)

Sendable Types

๋ผ๋Š” ๊ฒƒ๋„ ์žˆ๋Š”๋ฐ 2022-09-12 ๊ฐ€์žฅ ์ตœ๊ทผ์— ๋ฌธ์„œ์— ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์œ„ ํ”„๋กœํผํ‹ฐ์™€ ๋ฉ”์†Œ๋“ค์— ๋Œ€ํ•ด ๋” ๊ณต๋ถ€ํ•ด๋ณด๊ณ  ๋‹ค์‹œ ์ฝ์–ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค!