[Concurrency] Task-1
1. 기존 동시성 프로그래밍(GCD)
- 대기열 / 큐를 사용하였다
-
대기열에 넣어서 2번 cpu에 일을 시키는 방식으로 비동기 처리
// 기존 동시성 프로그래밍(GCD) // 기본적으로 다른 쓰레드로 보내지 않았다면 1번 쓰레드(cpu)에서 실행한다 print("메인 쓰레드에서 실행 - 1") print("메인 쓰레드에서 실행 - 2") // 무조건 2번(또는 특정의 백그라운드) 스레드로 보내서 실행하겠다는 의미(클로저 내에서는 순차적 실행) DispatchQueue.global().async { print("백그라운드 쓰레드에서 실행 - 1") print("백그라운드 쓰레드에서 실행 - 2") } print("메인 쓰레드에서 실행 - 3") print("메인 쓰레드에서 실행 - 4")
2. Swift Concurrency(asyc/await/task)
Task
- 비동기적인 일처리를 할 수 있는 하나의 실행 작업 단위를 만드는 것이다.
-
기존의 GCD방식과 거의 유사하다. (2번 스레드로 보내서 일을 시킨다는 점에서)
print("메인 쓰레드에서 실행 - 1") print("메인 쓰레드에서 실행 - 2") Task { print("백그라운드 쓰레드에서 실행 - 1") print("백그라운드 쓰레드에서 실행 - 2") } print("메인 쓰레드에서 실행 - 3") print("메인 쓰레드에서 실행 - 4")
서로 다른 작업 단위
- 각각의 Task는 다른 작업 단위이다.
- 예를 들어 Task1은 2번 Cpu에 일을 시킬 수 있고 동시에 Task2는 3번 Cpu에게 일을 시킬 수 있다.
- Task는 병렬적으로 동시에 일을 할 수 있도록 해준다.
-
Task 클로저 내부는 순차적으로 실행된다.
// Task 1: // 2번 cpu Task { print("백그라운드 쓰레드에서 실행 - 1") print("백그라운드 쓰레드에서 실행 - 2") } // Task 2: 3번 cpu Task { print("백그라운드 쓰레드에서 실행 - 3") print("백그라운드 쓰레드에서 실행 - 4") } // -> 2번, 3번 병렬적으로 동시에 일을 할 수 있게 만들어준다
Task를 변수/상수에 담을수도 있다
- Task 자체를 타입으로 생성을 해서 변수에/상수에 담을 수 있다.
- 작업을 변수/상수에 담으면 변수에 접근하여 cancel 메서드(작업 취수) 사용 가능.
-
Never: 일반적인 구조에서 에러 발생하지 않는다.
let task: Task<Void, Never> = Task { print("비동기적인 일 - 7") print("비동기적인 일 - 8") } task.cancel()
Task 작업의 결과로 문자열을 리턴할 수 있다
-
task에서 문자열을 접근할 수 있다.
let task: Task<String, Never> = Task { print("비동기적인 일 - 1") print("비동기적인 일 - 2") return "문자열" } // task.value -> Task 성공의 결과값에 접근 // task.result -> Task를 Result 타입으로 변환 가능
동기 함수 내에서 비동기적인 일처리를 할 수 있다
-
함수 내부에 작업을 만들어서 비동기적인 일처리 가능하다.
func doSomething() { Task { try await Task.sleep(for: .seconds(2)) print("함수 내부의 비동기적인 일 - 1") print("함수 내부의 비동기적인 일 - 2") } } /* 함수 내부의 비동기적인 일 - 1 함수 내부의 비동기적인 일 - 2 */ func doSomething2() { print("함수 내부의 동기적인 실행 - 1") // 2번 Cpu에서 비동기적인 일 실행 가능 Task { // 2초동안 일을 멈추는 코드.. 즉 2초정도 걸림 try await Task.sleep(for: .seconds(2)) print("함수 내부의 오래걸리는 일") } print("함수 내부의 동기적인 실행 - 2") } /* 함수 내부의 동기적인 실행 - 1 함수 내부의 동기적인 실행 - 2 함수 내부의 오래걸리는 일 */
작업의 우선순위는 6가지가 존재한다
- 우선순위 지정은 선택사항이다.
-
우선순위를 사용하지 않는 방법이 일반적이다.
// 선택사항 우선순위 지정가능 task = Task(priority: .우선순위) { // 비동기적인 일(비동기 함수 실행) } // 현재 우선순위를 볼 수 있다 Task(priority: .userInitiated) { print("우선순위: \(Task.currentPriority)") } /// 작업 실행 우선 순위의 종류 /// =============================== /// TaskPriority.userInitiated - 25 /// TaskPriority.high - 25 /// TaskPriority.medium - 21 /// TaskPriority.low - 17 /// TaskPriority.utility - 17 /// TaskPriority.background - 9 /// ===============================
작업은 지금 실행 컨텍스트(실행되고 있는 환경)의 메타데이터를 그대로 상속해서 사용한다(구조적 동시성은 아니다)
-
내부에서 자동으로 현재의 컨텍스트(어떤 환경에서 실행되는지)를 파악하고
-
우선순위 -> 실행액터 -> 로컬변수(Task-Local 변수)
-
취소는 상속되지 않는다.
let task = Task(priority: .background) { sleep(2) print("비동기적인 일 실행: \(Task.currentPriority)") print("Task 내부에서 취소 여부: \(Task.isCancelled)") // 내부의 작업 -> 부모 작업의 메타데이터(우선 순위 등)를 상속 사용(취소는 상속 불가) // 작업 안에서 작업을 다시 생성하는 것은 구조화를 시키지 않는다(하위 작업이 되는 것이 아니다) Task { print("비동기적인 일 실행: \(Task.currentPriority)") print("Task 내부의 Task에서 취소 여부: \(Task.isCancelled)") } } // task.cancel() 하더라도 내부 Task는 실행된다 -> 상속이 안되기 때문 task.cancel() /* print("비동기적인 일 실행: \(Task.currentPriority)") print("Task 내부의 Task에서 취소 여부: \(Task.isCancelled)") */
특징
-
작업은 비동기적인 일처리를 위한 기본단위이다. (모든 비동기적인 일처리는 Task 일부)
-
우선순위 지정은 선택사항이다.
-
구조체로 만들어져있다.
-
인스턴스를 생성하자마자 operation 파라미터에 해당하는 클로저를 전달하면서, 작업(Task)을 생성 및 클로저로 전달된 작업을 즉시 실행한다.
-
즉 클로저를 할당하여 작업을 만들고 클로저를 바로 실행하여 Task를 생성한다.
-
Task 클로저 내에서 비동기적인 일 수행 가능하다.
-
비동기 실행 컨텍스트(비동기 함수기 실행될 수 있는 환경)을 만드는 것이다.
-
Swift Concurrency에서 작업 Task는 기본 단위이다.
-
Task는 트레일링 클로저 형태로 사용한다.
-
작업 클로저를 생성하자마자 작업 즉시 실행한다.
-
https://developer.apple.com/documentation/swift/task
// 에러를 던지지 않는 함수 Task(priority: <#T##TaskPriority?#>, operation: <#T##() async -> Sendable#>) // 에러를 던질 수 있는 함수 Task(priority: <#T##TaskPriority?#>, operation: <#T##() async throws -> Sendable#>)
Leave a comment