# Concurrency
비동기 코드와 병렬코드를 구조적으로 지원하는 방법이 스위프트에 내장되어 있다. 비동기 코드는 네트워킹 작업같은 긴 작업 중에도 짧은 UI 업데이트를 할 수 있도록 한다.병렬 코드는 여러개의 코드를 동시에 실행하는 것을 의미한다. 예를들어 4코어 컴퓨터는 각 코어당 하나씩 수행을 맡아서 동시에 4개의 코드를 실행할 수 있다. 병렬코드와 비동기 코드를 사용하는 프로그램은 memory-safe한 코드를 쉽게 작성할 수 있도록 한다.
동시성을 추가하는 것은 디버그하기 어렵게 만들 수도 있다. 그러나 스위프트 에서는 컴파일 타임에 잡아낼 수 있다.
>스위프트에서 스레드와 직접 상호작용 하지 않는다. 자신이 실행되던 쓰레드에 다른 비동기 함수가 먼저 들어와서 실행될 수 있도록 한다. 비동기 함수가 실행되면, 어느 쓰레드에서 실행되는지 보장할 수 없다.
스위프트가 지원하는 동시성 언어를 사용하지 않으면 읽기 힘든 코드가 된다. completion handler의 연속이 될 것이다.
+ async를 사용하면 throw를 해줄 수 있으니 컴파일 타임에 오류를 잡아낼 수 있는 것이다. - 언쳉
## Defining and Calling Asynchronous Functions
비동기 메서드는 실행중 중지할 수 있다. 동기 함수와는 대조적인 것이다.
비동기 메서드 임을 명시해주는 방법이다.
```swift
func listPhotos(inGallery name: String) async -> [String] {
let result = // ... some asynchronous networking code ...
return result
}
```
throw가 있으면 async throw 순으로 작성한다.
에러가 날 수 있는 곳에 try 를 작성해주는 것처럼 중지가 될 수 있는 곳에 `await` 키워드를 붙여준다.
```swift
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
```
실행순서이다.
1. `listPhotos()` 메서드가 실행되고, 반환될 때까지 기다린다.
2. 위 코드가 중지된 동안 다른 동시성 코드가 실행된다.
2-1.
4. `listPhotos()` 가 반환되고, 이 시점에서 다시 시작한다.
5. sortedNames와 name 은 동기코드이다. 평범하게 실행된다.
6. `downloadPhoto()` 메서드가 실행되고 반환될때까지 기다린다. 마찬가지로 다른 동시성 코드를 실행할 기회를 준다.
7. 위 코드가 반환되고, photo로 할당된 뒤, show를 한다.
쓰레드 양보 라고도 불린다. 왜냐하면 스위프트는 현재 스레드를 중지하고 대신 다른 코드를 현재스레드에 실행시키는 것이다.
await이 쓰여진 코드는 실행을 중지할 수 있어야 해서, 어떤 특정 장소에서만 비동기 함수를 호출할 수 있다 :
- Code in the body of an asynchronous function, method, or property.
- Code in the static main() method of a structure, class, or enumeration that’s marked with @main.
- Code in an unstructured child task, as shown in Unstructured Concurrency below.
중단가능한 시점 사이의 코드는 다른 동시코드로부터 방해될 가능성 없이. 연속적으로 실행된다.
```swift
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() 와 remove() 사이에 다른 코드가 실행될 수 없다. firstPhoto 가 임시적으로 두 갤러리에 동시에 존재하게 된다. 이는 앱의 불변성을 위반한다. 이 메서드 사이에 await 코드가 오면 안된다는 것을 명시하기 위해 이렇게 작성할 수 있다.
```swift
func move(_ photoName: String, from source: String, to destination: String) {
add(photoName, to: destination)
remove(photoName, from: source)
}
// ...
let firstPhoto = await listPhotos(inGallery: "Summer Vacation")[0]
move(firstPhoto, from: "Summer Vacation", to: "Road Trip")
```
move() 메서드는 동기적이기 때문에, 중지가능 시점을 포함할 수 없다는 보장을 할 수 있다.
## Asynchronous Sequences
```swift
import Foundation
let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
print(line)
}
```
AsyncSequence를 채택해주면 이렇게 사용할 수 있다.
## Calling Asynchronous Functions in Parallel
await을 통해 비동기메서드를 호출하면 한번에 한 작업밖에 수행할 수 없다.
```swift
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)
```
이렇게 하면 비동기 메서드임에도 불구하고 불필요하게 기다려 주어야 한다. 동시에 실행시켜줄 수 없나?
```swift
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)
```
- 다음 코드줄이 비동기 메서드의 결과에 의존한다면 await을 사용해라. (예를 들면 결과를 할당받아 다음 줄에서 프린트를 한다거나)
- 이후에 비동기 메서드의 결과가 필요한 것이 아니면 async-let을 이용해라. 그럼 병렬 수행을 할 수 있다.
- await과 async-let 둘다 중지되었을 때 다른 코드를 수행할 수 있다.
#### 내가 짜본 예시
```swift
func listPhotos() async throws -> [String] {
print("네트워킹 작업중 ...")
try await Task.sleep(nanoseconds: 3000000000)
return ["1", "2", "3"]
}
print("start")
async let result = listPhotos()
async let result2 = listPhotos()
async let result3 = listPhotos()
let strs = try await [result, result2, result3]
print(strs)
```
병렬 작업을 하는 방법은 위 코드와 같이 async let 으로 선언하고 마지막에 기다려주면 3개의 작업이 동시에 실행된다.
## Tasks and Task Groups
task는 비동기로 실행될 수 있는 단위이다. task 는 자식을 가질 수 있다.
이는 구조적 동시성이라고 부른다.
정확성에 대한 일부 책임을 져야 하지만, 작업 간의 명시적인 부모-자식 관계를 통해 Swift는 취소 전파와 같은 몇 가지 동작을 처리할 수 있으며, Swift는 컴파일 타임에 일부 오류를 감지할 수 있습니다.
```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)}
}
}
```
### Unstructured Concurrency
### Task Cancellation
## Actors
## Sendable Types
한 동시성 도메인에서 다른 곳으로 공유될 수 있는 타입을 Sendable 타입이라 한다.
가변상태를 포함하는 몇몇 타입은 동시성 도메인 사이에 공유될 때 안전하지 않다.
Sendable이 되기위한 3가지 방법:
- 값타입 이어야 하고, 가변상태가 다른 sendable 데이터로 만들어진 것이어야 한다. 예를들어 sendable한 저장프로퍼티를 가지는 구조체, 또는 sendable한 associated value를 가진 enum
- 가변상태를 일체 가지면 안되며, 불변상태는 sendable 데이터로 만들어진 것이어야 한다. 예를들어 읽기전용 프로퍼티만 가지는 클래스 또는 구조체
- 가변상태에 안정성을 보장해야 한다. 예를들어 @MainActor 마크된 클래스, 또는 특정 스레드 또는 큐의 프로퍼티에 대한 액세스를 직렬화하는 클래스.
## Reference
- [Swift Programming Language - Concurrency](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html)