Irbisa · Шпаргалка
Swift Concurrency
async/await, Task, акторы, structured concurrency, AsyncSequence
Чем конкурентность отличается от параллельности, а чем синхронное выполнение отличается от асинхронного?
Что такое GCD, чем отличаются serial- и concurrent-очереди и чем
syncотличается отasync?let serial = DispatchQueue(label: "com.app.cache") // serial by defaultlet concurrent = DispatchQueue(label: "com.app.io", attributes: .concurrent) serial.async { print("1") } // returns immediatelyserial.async { print("2") } // always runs after "1"…Как работает
async/awaitв Swift и чем он отличается от completion handler’ов?// Before: completion handlerfunc loadUser(id: String, completion: @escaping (Result<User, Error>) -> Void) { // ...} // After: async/await…Как вызвать
async-функцию из синхронного кода и можно ли вызывать синхронный код изasync-функции?@MainActor@Observablefinal class ProfileViewModel { var profile: Profile? private var loadTask: Task<Void, Never>?…Что выведет этот код, если
test()вызван на главном потоке, и что изменится, если вызвать его с фоновой очереди?func test() { print("A") DispatchQueue.main.async { print("B") } print("C") DispatchQueue.main.sync { print("D") } print("E")}test() // called on the main threadКакие бывают классы Quality of Service и как они влияют на планирование?
Что такое
DispatchGroupи когда его использовать?func loadAll(ids: [String], completion: @escaping ([User]) -> Void) { let group = DispatchGroup() let lock = NSLock() var users: [User] = [] for id in ids {…Чем GCD отличается от
OperationQueueи когда стоит выбратьOperationQueue?Чем race condition отличается от data race и от deadlock и как предотвратить каждую из проблем?
Что такое семафор, чем он отличается от мьютекса и когда
DispatchSemaphoreстановится плохой идеей?import Synchronization // Mutex: one thread at a time, and the owner unlocksfinal class Counter: Sendable { private let value = Mutex(0) func increment() { value.withLock { $0 += 1 } }…Что такое structured concurrency и когда использовать
async let, а когда task group?// Fixed fan-out: three different requests in parallelfunc loadDashboard() async throws -> Dashboard { async let user = api.user() async let posts = api.posts() // all three start right here async let news = api.news() return try await Dashboard(user: user, posts: posts, news: news)…Чем
Task { }отличается отTask.detachedи как определяется приоритет задачи?@MainActor@Observablefinal class FeedModel { var items: [Item] = [] func refresh() {…Как работает отмена задач в Swift Concurrency?
func processAll(_ items: [Item]) async throws { for item in items { try Task.checkCancellation() // stop between units of work try await process(item) }}…Найдите баг: после того как экран вызывает
stop(), тикер продолжает дёргать API, а загрузка CPU резко растёт.// api: a Sendable global whose fetchPrice() is async throws -> Decimal@MainActorfinal class PriceTicker { private var task: Task<Void, Never>? private(set) var price: Decimal = 0 func start() { task = Task { while true { if let p = try? await api.fetchPrice() { price = p } try? await Task.sleep(for: .seconds(5)) } } } func stop() { task?.cancel() }}loadUser()бросает ошибку через 0,1 с, аloadPosts()идёт 10 с: что будет с запросом постов и когда вернётсяloadScreen()?func loadScreen() async throws -> Screen { async let posts = api.loadPosts() // takes 10 s async let user = api.loadUser() // throws after 0.1 s let u = try await user let p = try await posts return Screen(user: u, posts: p)}Нужно скачать 500 картинок, но не больше 5 одновременно: как ограничить конкурентность в task group?
func downloadAll(_ urls: [URL], maxConcurrent: Int = 5) async throws -> [URL: Data] { try await withThrowingTaskGroup(of: (URL, Data).self) { group in var pending = urls.makeIterator() var results: [URL: Data] = [:] // Seed the first `maxConcurrent` downloads…Как добавить таймаут к
await-вызову, если у API нет параметра таймаута?struct TimeoutError: Error {} func withTimeout<T: Sendable>( _ duration: Duration, operation: @escaping @Sendable () async throws -> T) async throws -> T {…Что такое актор, как он предотвращает гонки данных и почему его методы снаружи вызываются через
await?actor BankAccount { let id: UUID private(set) var balance: Decimal = 0 init(id: UUID) { self.id = id }…Что гарантирует
@MainActor, почему не стоит помечать им всё подряд и как увести работу с него?@MainActor@Observablefinal class FeedViewModel { var items: [Item] = [] func reload() async throws {…Что такое
Sendableи какие типы соответствуют ему автоматически?import Synchronization struct User: Sendable { // all stored properties are Sendable let id: UUID var name: String}…Как обернуть API на completion handler’ах в
async/awaitи чем checked-continuation отличается от unsafe?// Legacy APIfunc fetchUser(id: String, completion: @escaping (Result<User, Error>) -> Void) { // ...} // Async wrapper…Что такое
AsyncSequenceиAsyncStreamи когда их использовать?// LocationTracker: a Sendable wrapper around a delegate-based APIfunc locationUpdates() -> AsyncStream<CLLocation> { AsyncStream(bufferingPolicy: .bufferingNewest(1)) { continuation in let tracker = LocationTracker() tracker.onUpdate = { continuation.yield($0) } continuation.onTermination = { _ in tracker.stop() } // cancelled or finished…Этот актор всё равно скачивает одну и ту же картинку дважды, когда её одновременно просят два view: почему и как это исправить?
// downloader: a Sendable global dependency whose fetch(_:) is asyncactor ImageCache { private var cache: [URL: UIImage] = [:] func image(for url: URL) async throws -> UIImage { if let cached = cache[url] { return cached } let image = try await downloader.fetch(url) // suspension point cache[url] = image return image }}Что такое cooperative thread pool и почему его блокировка через
DispatchSemaphore.wait()илиThread.sleepсчитается багом?// wrong: called from async code, this blocks a pool thread; one per core and the app is wedgedfunc loadSync() -> Data { let semaphore = DispatchSemaphore(value: 0) var result = Data() Task { result = (try? await api.download()) ?? Data()…Как защитить общее изменяемое состояние (актор, lock или serial-очередь) и как выбрать между ними?
import Synchronization // Hot, synchronous, tiny critical section: a lockfinal class HitCounter: Sendable { private let counts = Mutex<[String: Int]>([:]) func record(_ key: String) { counts.withLock { $0[key, default: 0] += 1 } }…