- synchronous function์ ํธ์ถํ๋ฉด, ํจ์๊ฐ ์ข ๋ฃ๋ ๋๊น์ง thread๊ฐ block๋๋ค.
- asynchronous function์ ํธ์ถํ๋ฉด, ํจ์๊ฐ ๋์ํ๋ ์ค์ thread์๊ฒ ๋ค๋ฅธ ์ผ์ ์ํค๊ณ (suspension) ํจ์๊ฐ ์ข
๋ฃ๋๋ฉด ํด๋น ์ค๋ ๋์ ์๋ ค์ค๋ค.
- by completion handler,
- delegate callbacks
- or returning value (with async keyword)
- asynchronous function์ ์ค์ ํจ์๊ฐ ์๋ฃ(complete)๋๊ธฐ ์ ์ ๋ฐ๋ก
return
ํ๋ค. - parallel์ ์ค์ ๋ก ๋์์(at the same time) ์ฌ๋ฌ ์ฝ๋๋ฅผ ๋์์ํด์ ์๋ฏธํ๋ค.
- ์ฃผ๋ก swift์์ concurrency๋ parallel๊ณผ concurrent๋ฅผ ํจ๊ป ์ง์นญํ๋ค.
- medium How to use Appleโs Grand Central Dispatch library to multitask threads like a Pro
- Introduced in iOS4 (2010)
- Objective-c๋ถํฐ ์ฌ์ฉํ๋ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ API
- implementation of task parallelism based on the thread poll pattern
- Thread poll์ developer๊ฐ ๊ด๋ฆฌํ๋ ๋์ OS๊ฐ ์ง์ ๊ด๋ฆฌํด์ฃผ๋ ๊ฒ
- GCD works by allowing specific tasks in a program that can be run in parallel to be queued up for execution and, depending on avaliability of processing resources, scheduling then to execute on any of available processor cores
Dispatch Queues are objects that maintain a queue of tasks, either anonymous code blocks or function, and execute these tasks in their turn
- https://www.raywenderlich.com/5370-grand-central-dispatch-tutorial-for-swift-4-part-1-2
- Library๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ๋ฌ๊ฐ์ queue๋ค์ ์์ฑํ๋ค.
- Main queue: runs on the main thread and is a serial queue
- Global queues: ์ ์ฒด ์์คํ ์ด ๊ณต์ ํ๋ concurrent queue. ๊ฐ๊ธฐ ๋ค๋ฅธ priority๋ฅผ ๊ฐ๋ ๋ค ๊ฐ์ ํ๊ฐ ์๋ค.
- client๋ ์ง์ queue๋ฅผ ์์ฑํ ์ ์๋ค. (Custom queues)
- serial or concurrent
- ํด๋น Request๋ค์ ์ค์ ๋ก๋ global queue๋ค ์ค ํ๋์์ ์๋๋๋ค.
- FIFO queue
- Serial ๋๋ Concurrent Type์ด๋ค.
- concurrent queue์์๋ task๋ฅผ ์์๋๋ก ์คํ์ ์์ํ๋ค.
- ๋จ, suspend ๋ ์ ์๊ธฐ ๋๋ฌธ์ ์ธ์ ๋ค์ task๊ฐ ์์๋ ์ง, ๋ช ๊ฐ์ task๊ฐ ๋์์ ์๋ํ๊ณ ์๋ ์ง๋ ์ ์ ์๋ค.
- GCD๋ task๋ค์ ๋ค๋ฅธ core์์ ์คํ์ํค๊ฑฐ๋, context switch๋ฅผ ํตํด ๋ค๋ฅธ task๋ฅผ ์คํํ ์๋ ์๋ค.
- ์ฝ๋ ๋ธ๋ญ ํ์์ผ๋ก ํ์คํฌ๋ฅผ ์ ๋ฌ๋ฐ๋๋ค.
- ์ ๋ฌ๋ ํ์คํฌ๋ค์ ์์คํ ์ ์ํด ๊ด๋ฆฌ๋๋ thread poll์์ ์๋ํ๋ค.
- Avoiding Excessive Thread Creation
- ํ์ฌ ํ ๋น๋ ํ์คํฌ๋ก thread๊ฐ ๋ธ๋ฝ๋๋ฉด, ์์คํ ์ ์๋ก์ด ์ค๋ ๋๋ฅผ ์์ฑํ์ฌ ๋ค๋ฅธ ํ ์คํฌ๋ค์ ๋์์ํจ๋ค.
- ๋๋ฌด ๋ง์ private concurrent dispatch queue๋ฅผ ์์ฑํด๋ ๋๋ฌด ๋ง์ ์ค๋ ๋๊ฐ ์์ฑ๋ ์ ์๋ค.
- WWDC2021์์ ์ฒ์ ์๊ฐ๋จ
- CPU๊ฐ ๋ธ๋ฝ๋ ๋๋ง๋ค ์๋ก์ด ์ค๋ ๋๋ฅผ ์์ฑํ๋ฏ๋ก thread explosion์ด ๋ฐ์ํ ์ ์๋ค.
- thread๋ ๊ฐ์ ๋ฆฌ์์ค๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฏ๋ก memory overhead
-
- excessive context switch
- completion handler๋ก ์ฝ๋์ ์ง๊ด์ฑ์ด ๋จ์ด์ง๊ณ , human-error์ ์ํ์ฑ์ด ๋์์ง๋ค.
func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
let request = thumbnailURLRequest(for: id)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(nil, error)
} else if (response as? HTTPURLResponse)?.statusCode != 200 {
completion(nil, FetchError.badID)
} else {
guard let image = UIImage(data: data!) else {
// completion์ ํธ์ถํ์ง ์์์ caller๋ ์๋ฌ ๋ฐ์ ์ ๋ฌด๋ฅผ ์ ๋ฌ ๋ฐ์ง๋ชปํ๋ค.
return
}
image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in
guard let thumbnail = thumbnail else {
completion(nil, FetchError.badImage)
return
}
completion(thumbnail, nil)
}
}
}
task.resume()
}
- the keyword
await
indicates that async function might suspend there - async function
- will finish and return control to your function
- but it can give up control of the thread in an entirely different way: by suspending
- (normal function์์๋ ํจ์๋ฅผ ์ข ๋ฃํ๋ ๊ฒ ์ธ์ ์ ์ด๊ถ์ ๋๊ธธ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๋ค)
- async function์ ํธ์ถ๋๋ฉด thread์ ์ ์ด๊ถ์ ๋ถ์ฌ๋ฐ๊ณ , suspension์ ํตํด ํด๋น ์ ์ด๊ถ์ system์ ๋๊ธด๋ค. (caller function์ ๋๊ธฐ๋ ๊ฒ ์๋๋ค!)
- system์ thread์ ๋ค๋ฅธ ์์ ๋ค์ ํ ๋นํ๋ค๊ฐ, suspension๋ function์ ๋ค์ ์งํํ๋ค.
await
ํค์๋๋ suspensionable point๋ฅผ ์๋ฏธํ๋ค. ํด๋น point๋ฅผ ๊ธฐ์ ์ผ๋ก thread๊ฐ ๋ฐ๋ ์๋ ์๊ณ , ํด๋น suspension์ ๋ค๋ฅธ ์์ ์ด ์คํ๋ ์ ์๋ค.- await์ async ํจ์์์๋ง ์ด์ฉ ๊ฐ๋ฅํ๋ค.
async
ํค์๋๋ ํจ์๊ฐ suspensionableํจ์ ์๋ฏธํ๋ค. ํจ์๊ฐ suspension๋๋ฉด caller๋ suspension๋๋ค. ์ฆ, async function์ caller๋ async function ์ด์ด์ผ ํ๋ค.
- ๊ฐ์ ์ฝ๋๋ฅผ async, await์ ์ด์ฉํด์ ํํํ๋ฉด
func fetchThumbnail(for id: String) async throws -> UIImage {
let request = thumbnailURLRequest(for: id)
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw FetchError.badID }
let maybeImage = UIImage(data: data)
// property๊ฐ async๋ก ์ ์๋์ด์๋ค.
guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError.badImage }
return thumbnail
}
- ๊ฐ๋จํด์ง๊ณ ,์ง๊ด์ ์ด์ด์ง๋ค.
- ๊ธฐ์กด swift์ error handling ๋ฐฉ์์ ๊ทธ๋๋ก ์ด์ฉํ ์๋ ์๋ค.
- task์ dependencies๋ค์ ํธ๋ํนํ ์ ์๋ค.
- ์ปดํ์ผ ๋จ๊ณ์์ ๋๊ธฐํ ๊ด๋ จ ์๋ฌ๋ฅผ ๋ฐ์์ํจ๋ค.
- thread๋ 6๊ฐ(for iPhone, cpu ๊ฐ์๋งํผ)๋ฐ์ ์๊ณ , lightweight thrad์ธ continuation(function call)์ ์ค์์นํ๋ค.
- non-async function์์๋ stack์ ์ด์ฉํด์ ํธ์ถ๋ ๋๋ง๋ค ํด๋น ํจ์๋ฅผ pop!
- async function์์๋ stack๊ณผ heap์ ์ด์ฉ,
- suspension point(marked with
await
) ๊ฐ ์ ๋ฌ ๋ ํ์๊ฐ ์๋ local variable์ stack์ ์ ์ฅํ๊ณ - suspension point๊ฐ ์ ๋ฌ๋ variable์ heap์ ์ ์ฅ๋๋ค
- await ํจ์๋ฅผ ํธ์ถํ๊ฑฐ๋ continuation switch๋ฅผ ํตํด ์ปจํ ์คํธ๊ฐ ๋ณ๊ฒฝ๋๋ฉด stack์ pushํ๋ ๋์ , replaceํ๋ค.
- suspension point(marked with
- Performance
- concurrency๋ ์ถ๊ฐ์ ์ธ ๋ฉ๋ชจ๋ฆฌ์ ๋ก์ง์ ํ์๋ก ํ๋ค.
await
๊ตฌ๊ฐ์์ thread๊ฐ ๋ณ๊ฒฝ๋ ์ ์์์ ๊ธฐ์ตํด์ผ ํ๋ค.- atomicity๊ฐ ๊นจ์ง๊ณ ,
- thread specific data๊ฐ ๋ณ๊ฒฝ๋ ์ ์๋ค.
- swift์ runtime contract์ธ Forward progress(hreads can always make forward progress)๋ฅผ ์ง์ผ์ค์ผ ํ๋ค.
- ์ฐ์ ์์๊ฐ ๋์ ํ์คํฌ๊ฐ ๋ ๋ฎ์ ์ฐ์ ์์์ ํ์คํฌ ๋๋ฌธ์ ์คํ๋์ง ๋ชปํ๋ ๊ฒ
- ๋ task๊ฐ ๋ฆฌ์์ค๋ฅผ ๊ณต์ ํ ๋ ๋ฐ์ํ๋ค. ์ฐ์ ์์๊ฐ ๋์ ํ์คํฌ๋ ์ฐ์ ์์๊ฐ ๋ฎ์ ํ์คํฌ๊ฐ ์ข ๋ฃ๋์ด ํด๋น ๋ฆฌ์์ค๋ฅผ ์ด์ฉํ ์ ์๊ฒ ๋ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ค.
- GCD๋ low priority queue์ QoS๋ฅผ ํต์งธ๋ก ๋์ฌ์ ์ด๋ฅผ ํด๊ฒฐํ๋ค. FIFO ํ๋ฅผ ์ด์ฉํ๊ธฐ ๋๋ฌธ
- ๊ทธ๋ ๊ฒ ๋๋ฉด, ์์์ ๊ณต์ ํ๋ low priority task ์์ ์๋ ์์ ๋ค์ด ๋ชจ๋ ์คํ๋ ํ,
- low priority task๊ฐ ์คํ๋๊ณ
- high priority task๊ฐ ์คํ๋๋ค.
- Swift Concurrency(async, await)๋ FIFO queue ๋์ ์์คํ ์ด ์ง์ ์์ ์ ํ ๋นํ๊ธฐ ๋๋ฌธ์ ์ฐ์ ์์๊ฐ ๋์ ํ์คํฌ๊ฐ ๋ฐ๋ก ๋จผ์ ์คํ๋ ์ ์๋ค.
- a unit of work that can be run asynchronously as part of your program
- A unit of asynchronous work.
- ์๋์ผ๋ก parallel ๋์์ ์ง์ํจ
- ์์ฑ๋์๋ง์ ์์ ์ ์์ํ๊ธฐ ๋๋ฌธ์ ์ง์ ์์ํ๊ฑฐ๋, ์ค์ผ์ฅดํด์ค ํ์๋ ์๋ค.
- Swift compiler๊ฐ ์๋์ผ๋ก concurrency bug๋ค์ ์ก์์ค
- parent task๋ child tasks๋ค์ด ๋ชจ๋ ์๋ฃ๋ ์ํ์์๋ง ์ข ๋ฃ ๊ฐ๋ฅํ๋ค.
- ๋ง์ฝ child task ์ค ํ๋๊ฐ ๋น์ ์์ ์ผ๋ก ์ข ๋ฃ๋๋ค๋ฉด, parent task๋ error๋ฅผ ๋์ง๋ค. ๋จ์ unawaited task๋ค์ ๋ํด์๋ cancel์ ๋งํฌํด๋๋๋ค.
- cancel ๋งํฌ๋ task์ ๊ทธ subtask๋ค์๊ฒ response๊ฐ ํ์์์์ ์๋ฆฐ๋ค.
- Cancellation is cooperative
- cancellation์ ๊ณ ๋ คํ ์ฝ๋
- cancel์ด ์ผ์ด๋ ๊ฒฝ์ฐ result์ ์ผ๋ถ๋ฅผ ๋ฆฌํดํ๋๋ก ์ค๊ณํ์๋ค
fetchOneThumbnail
ํจ์๊ฐ ๋ชจ๋ ์๋ฃ๋ ์ดํ์ ๋ค์ loop๊ฐ ์์๋๋ค.
- more flexibility (than async-let)
- A task group is a form of structured concurrency that is designed to provide a dynamic amout of concurrency by calling the withThrowingTaskGroup function
- group์ ์ถ๊ฐ๋์๋ง์ task๋ ์์๋๋ค.
- dictionary์ ํ ๋ฒ์ ํ๋๋ง ์ ๊ทผํ ์ ์๊ธฐ ๋๋ฌธ์ Data-race ๊ฐ ๋ฐ์ํ๋ค.
- swift compiler๊ฐ ์ก์์ค ใฑใ ใท
- Task๋ฅผ ์์ฑ๋ ์ด์ฉํ๋ closure๋
@Sendable
closure ํ์ ์ด๋ค. - Sendable closure์์๋ mutliple varialble์ ์ด์ฉํ ์ ์๋ค.
- ๋์ value type, actors, or class that implement their own synchronization์ ์ด์ฉํด์ผํ๋ค.
- child tasks inherit priority from parent
- when tasks need to launch for non-async contexts
- when tasks live beyond the confines of a single scope
- unscoped lifetime
- do not inherit anything form their originating context
- optional parameters control priority
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 /* OK */ else {
throw MyNetworkingError.invalidServerResponse
}
let (location, response) = try await URLSession.shared.download(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 /* OK */ else {
throw MyNetworkingError.invalidServerResponse
}
try FileManager.default.moveItem(at: location, to: newLocation)
- file์ ์๋์ผ๋ก ์ง์ฐ์ง ์์
var request = URLRequest(url: url)
request.httpMethod = "POST"
let (data, response) = try await URLSession.shared.upload(for: request, fromFile: fileURL)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 201 /* Created */ else {
throw MyNetworkingError.invalidServerResponse
}
- session, task delegate๊ฐ ๋ ๋ค ์์ผ๋ฉด task delegate๊ฐ ์๋ํ๋ค.
- Reference type
- actors allow only one task to access their mutable state at a time
- which makes it safe for code in multiple tasks to interact with the same instance for an 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
}
}
- keyword
actor
๋ฅผ ์ด์ฉํ์ฌ actor๋ฅผ ์ ์ํ ์ ์๋ค. - ์ธ์คํด์ค๋ฅผ ๋ง๋ค ๋์๋ structure๋ class๋ฅผ ์ด์ฉํ ๋์ ๋์ผํ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค.
- ๋ค๋ฅธ ์ ์, actor์ property๋ method์ ์ ๊ทผํ ๋์๋
await
์ ์ถ๊ฐํด์ผํ๋ค. ์ด๋ฅผ ํตํด ํด๋น ์ง์ ์ด suspension point์ผ ์ ์์์ ๋๋ฌ๋ธ๋ค. - actor๋ ํญ์ ํ ๋ฒ์ ํ๋์ task๋ง ์ ๊ทผ์ ํ์ฉํ๊ธฐ ๋๋ฌธ์, ๋ค๋ฅธ task๊ฐ ์ด๋ฏธ actor instance๋ฅผ ์ด์ฉํ๊ณ ์๋ค๋ฉด, ํด๋น ๋์์ suspension๋๋ค.
- actor ๋ด๋ถ์์ ์๊ธฐ ์์ ์ property์ ์ ๊ทผํ ๋์๋
await
์ ์ด์ฉํ์ง ์์๋ ๋๋ค.
let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
print(await logger.max)
// Prints "25"
Additional References
- WWDC21 Meet async/await in Swift
- Dev_pingu Meet async/await in Swift ํ๊ธ ์ ๋ฆฌ๋ณธ
- LIne engineering Swift Concurrency์ ๋ํด์
- Koyeb Introudction to Synchronous and Asynchrnous Processing