Last active
January 4, 2025 09:56
-
-
Save Noluk1991/09c59a3339e81984d1b15190619869d4 to your computer and use it in GitHub Desktop.
Code samples из презентации
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// Номера совпадают с красными номерами на слайдах в правом верхнем углу :) | |
// Номера sample-ов сделал по порядку, чтобы удобней было запускать по очереди. | |
// для запуска с Swift 6.0+: | |
extension Thread { | |
static var currentThread: Thread { | |
.current | |
} | |
} | |
// ### Swift — название секции | |
// слайд 95 | |
func sample1() { | |
for i in 0..<10_000 { | |
let q = DispatchQueue(label: "serial") | |
q.async { | |
print("\(i), \(Thread.currentThread)") | |
} | |
} | |
// 6, <NSThread: 0x6000034f0340>{number = 7, name = (null)} | |
// 4, <NSThread: 0x6000034fc080>{number = 4, name = (null)} | |
// ... | |
// 3064, <NSThread: 0x6000034cc540>{number = 513, name = (null)} 🤯 | |
// ... | |
} | |
// слайд 94 | |
func sample2() { | |
let q = DispatchQueue(label: "concurrent", attributes: .concurrent) | |
for i in 0..<10_000 { | |
q.async { | |
print("\(i), \(Thread.currentThread)") | |
} | |
} | |
// 0, <NSThread: 0x600002c60240>{number = 2, name = (null)} | |
// 1, <NSThread: 0x600002c64080>{number = 3, name = (null)} | |
// ... | |
// 256, <NSThread: 0x600002c42980>{number = 31, name = (null)} | |
// 145, <NSThread: 0x600002c65a40>{number = 65, name = (null)} | |
// ... | |
} | |
// ### Async-await | |
// слайды 84-82 | |
func sample3() { | |
func doAnotherAsyncWork() async { | |
print(2) | |
} | |
func doAsyncWork() async { | |
print(1) | |
await doAnotherAsyncWork() | |
print(3) | |
} | |
// 1 | |
// 2 | |
// 3 | |
} | |
// слайд 81 | |
func sample4() { | |
func doAnotherAsyncWork() async { | |
print("2 \(Thread.currentThread)") | |
} | |
func doAsyncWork() async { | |
print("1 \(Thread.currentThread)") | |
await doAnotherAsyncWork() // print(2) | |
print("3 \(Thread.currentThread)") | |
} | |
// result (не каждый запуск): | |
// если не воспроизводится: | |
// можно добавить в doAnotherAsyncWork try? await Task.sleep(nanoseconds: NSEC_PER_SEC * <ваше число секунд>) | |
//1 <NSThread: 0x600002600240>{number = 2, name = (null)} | |
//2 <NSThread: 0x600002600240>{number = 2, name = (null)} | |
//3 <NSThread: 0x60000260c080>{number = 3, name = (null)} | |
} | |
// слайд 81 | |
func sample5() { | |
func someSynchronousFunction() { | |
// await doAsyncWork() // ❌ если раскомментировать: | |
// 'async' call in a function that does not support concurrency | |
// Add 'async' to function 'someSynchronousFunction()' to make it asynchronous | |
} | |
} | |
// ### Task | |
// слайд 76 | |
func sample6() { | |
Task { | |
print("1 \(Thread.currentThread)") | |
await doAsyncWork() // 2 | |
await doAnotherAsyncWork() // 3 | |
print("4 \(Thread.currentThread)") | |
} | |
// 1 <NSThread: 0x6000035bc500>{number = 2, name = (null)} | |
// 2 <NSThread: 0x6000035bc500>{number = 2, name = (null)} | |
// 3 <NSThread: 0x6000035bc500>{number = 2, name = (null)} | |
// 4 <NSThread: 0x6000035bc500>{number = 2, name = (null)} | |
} | |
// слайд 75 | |
func sample7() { | |
Task { | |
print("1 \(Thread.currentThread)") | |
Task { | |
await doAsyncWork() // 2 | |
} | |
Task { | |
await doAnotherAsyncWork() // 3 | |
} | |
print("4 \(Thread.currentThread)") | |
} | |
// 1 <NSThread: 0x600001660180>{number = 2, name = (null)} | |
// 4 <NSThread: 0x600001660180>{number = 2, name = (null)} | |
// 2 <NSThread: 0x600001668140>{number = 3, name = (null)} | |
// 3 <NSThread: 0x600001660180>{number = 2, name = (null)} | |
} | |
// слайд 74 | |
func sample8() { | |
for i in 0..<10_000 { | |
Task { | |
print("\(i), \(Thread.currentThread)") | |
} | |
} | |
// 0, <NSThread: 0x600001214200>{number = 2, name = (null)} | |
// 12, <NSThread: 0x60000121c080>{number = 3, name = (null)} | |
// ... | |
// 43, <NSThread: 0x600001204140>{number = 11, name = (null)} | |
// ... | |
} | |
// слайд 73 | |
func sample9() { | |
for i in 0..<10 { | |
Task { | |
var isBlocked = false | |
while true { | |
if isBlocked == false { | |
print("blocked, \(Thread.currentThread)") | |
isBlocked = true | |
} | |
} | |
} | |
Task { | |
print("\(i), \(Thread.currentThread)") | |
} | |
} | |
// blocked, <NSThread: 0x600001718280>{number = 2, name = (null)} | |
// 0, <NSThread: 0x6000017140c0>{number = 3, name = (null)} | |
// blocked, <NSThread: 0x6000017140c0>{number = 3, name = (null)} | |
// 2, <NSThread: 0x60000170c080>{number = 5, name = (null)} | |
// 3, <NSThread: 0x600001714140>{number = 7, name = (null)} | |
// ... цифры 9 мы не увидим, все треды заняты работой. ⚠️ | |
} | |
// слайд 72 | |
func sample10() { | |
for i in 0..<10 { | |
Task { | |
while true { | |
// очень важная, но долгая работа | |
await Task.yield() | |
} | |
} | |
Task { | |
print("\(i), \(Thread.currentThread)") | |
} | |
} | |
// 3, <NSThread: 0x600000818240>{number = 3, name = (null)} | |
// ... | |
// 9, <NSThread: 0x60000081c680>{number = 6, name = (null)} ✅ | |
// ... | |
} | |
// слайд 70 | |
func sample11() { | |
Task { // .medium by default | |
// [.userInitiated, .high, .medium, .low, .utility, .background] | |
// RawValues: [25, 25, 21, 17, 17, 9] | |
Task(priority: .low) { | |
print("\(1), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
Task(priority: .high) { | |
print("\(2), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
} | |
// 2, <NSThread: 0x600001a40240>{number = 3, name = (null)}, p: TaskPriority.high | |
// 1, <NSThread: 0x600001a48080>{number = 2, name = (null)}, p: TaskPriority.low | |
} | |
// слайд 69-65 | |
func sample12() { | |
Task { | |
Task(priority: .high) { | |
Task { | |
print("\(1), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
Task(priority: .low) { | |
print("\(2), \(Thread.currentThread), p: \(Task.currentPriority)") | |
Task { | |
print("\(3), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
Task(priority: .medium) { | |
print("\(4), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
} | |
} | |
} | |
// 1, <NSThread: 0x600001fa0100>{number = 2, name = (null)}, p: TaskPriority.high | |
// 2, <NSThread: 0x600001fac080>{number = 3, name = (null)}, p: TaskPriority.low 👀 | |
// 3, <NSThread: 0x600001fac080>{number = 3, name = (null)}, p: TaskPriority.low 👀 | |
// 4, <NSThread: 0x600001fa0100>{number = 2, name = (null)}, p: TaskPriority.medium | |
} | |
// слайд 64 | |
func sample13() { | |
Task(priority: .high) { | |
Task { | |
print("\(1), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
await Task(priority: .low) { | |
print("\(2), \(Thread.currentThread), p: \(Task.currentPriority)") | |
await Task { | |
print("\(3), \(Thread.currentThread), p: \(Task.currentPriority)") | |
}.value | |
Task(priority: .medium) { | |
print("\(4), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
}.value | |
} | |
// | |
// 1, <NSThread: 0x600000074500>{number = 2, name = (null)}, p: TaskPriority.high | |
// 2, <NSThread: 0x60000007c080>{number = 3, name = (null)}, p: TaskPriority.high 👀 | |
// 3, <NSThread: 0x60000007c080>{number = 3, name = (null)}, p: TaskPriority.high 👀 | |
// 4, <NSThread: 0x60000007c080>{number = 3, name = (null)}, p: TaskPriority.medium | |
} | |
// слайд 62 | |
func sample14() { | |
Task(priority: .high) { | |
Task.detached { | |
print("\(1), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
Task(priority: .low) { | |
print("\(2), \(Thread.currentThread), p: \(Task.currentPriority)") | |
Task.detached { | |
print("\(3), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
Task(priority: .medium) { | |
print("\(4), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
} | |
} | |
// 1, <NSThread: 0x600003008040>{number = 3, name = (null)}, p: TaskPriority.medium 👀 | |
// 2, <NSThread: 0x600003004240>{number = 2, name = (null)}, p: TaskPriority.low | |
// 3, <NSThread: 0x600003008040>{number = 3, name = (null)}, p: TaskPriority.medium 👀 | |
// 4, <NSThread: 0x6000030080c0>{number = 4, name = (null)}, p: TaskPriority.medium | |
} | |
func sample14_v2() { // дополнительный пример, нет в слайдах | |
Task(priority: .high) { | |
Task.detached { | |
print("\(1), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
await Task(priority: .low) { | |
print("\(2), \(Thread.currentThread), p: \(Task.currentPriority)") | |
await Task.detached { | |
print("\(3), \(Thread.currentThread), p: \(Task.currentPriority)") | |
}.value | |
Task(priority: .medium) { | |
print("\(4), \(Thread.currentThread), p: \(Task.currentPriority)") | |
} | |
}.value | |
} | |
// 1, <NSThread: 0x600000c58080>{number = 2, name = (null)}, p: TaskPriority.medium | |
// 2, <NSThread: 0x600000c5c6c0>{number = 3, name = (null)}, p: TaskPriority.high 👀 | |
// 3, <NSThread: 0x600000c5c6c0>{number = 3, name = (null)}, p: TaskPriority.high 👀 | |
// 4, <NSThread: 0x600000c58080>{number = 2, name = (null)}, p: TaskPriority.medium | |
} | |
// слайд 61 | |
func sample15() { | |
Task { | |
let task = Task { | |
print("Task is cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
print("job done, \(Thread.currentThread)") | |
} | |
task.cancel() | |
} | |
// Task is cancelled: true, <NSThread: 0x600001c5c1c0>{number = 2, name = (null)} | |
// (через секунду) | |
// job done, <NSThread: 0x600001c5c1c0>{number = 2, name = (null)} ⁉️ | |
} | |
// слайд 60 | |
func sample16() { | |
Task { | |
let task = Task { | |
print("Task is cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
if Task.isCancelled { | |
print("Task was cancelled sorry, \(Thread.currentThread)") | |
} else { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
print("job done, \(Thread.currentThread)") | |
} | |
} | |
task.cancel() | |
} | |
// Task is cancelled: true, <NSThread: 0x6000018dc300>{number = 2, name = (null)} | |
// Task was cancelled sorry, <NSThread: 0x6000018dc300>{number = 2, name = (null)} ✅ | |
} | |
// слайд 59 | |
func sample17() { | |
Task { | |
let task = Task { | |
print("Task is cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
do { | |
try Task.checkCancellation() | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
print("job done, \(Thread.currentThread)") | |
} catch(let error) { | |
print("\(error), \(Thread.currentThread)") | |
} | |
print("end of task, \(Thread.currentThread)") | |
} | |
task.cancel() | |
} | |
// Task is cancelled: true, <NSThread: 0x6000028e0240>{number = 2, name = (null)} | |
// CancellationError(), <NSThread: 0x6000028e0240>{number = 2, name = (null)} | |
// end of task, <NSThread: 0x6000028e0240>{number = 2, name = (null)} | |
} | |
// слайд 58 | |
func sample18() { | |
Task { | |
let task = Task { | |
print("Task is cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
Task { | |
print("Inner task is cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
} | |
} | |
task.cancel() | |
} | |
// | |
// Task is cancelled: true, <NSThread: 0x60000161c280>{number = 2, name = (null)} | |
// Inner task is cancelled: false, <NSThread: 0x60000161c280>{number = 2, name = (null)} | |
} | |
func sample18_v2() { // дополнительный пример, нет в слайдах | |
Task { | |
let task = Task { | |
print("Task is cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
await Task { | |
print("Inner task is cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
}.value | |
} | |
task.cancel() | |
} | |
// Task is cancelled: true, <NSThread: 0x60000161c280>{number = 2, name = (null)} | |
// Inner task is cancelled: false, <NSThread: 0x60000161c280>{number = 2, name = (null)} | |
} | |
// слайд 57 | |
func sample19() { | |
Task { | |
let task = Task { | |
print("Task is cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
try await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // try вместо try? | |
// все ниже не выполнится | |
print("job done, \(Thread.currentThread)") | |
} | |
task.cancel() | |
} | |
// Task is cancelled: true, <NSThread: 0x60000161c280>{number = 2, name = (null)} | |
} | |
// слайд 56 | |
func sample20() { | |
Task { | |
let task = Task { | |
await withTaskCancellationHandler { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
print("Job done! \(Task.isCancelled), \(Thread.currentThread)") | |
} onCancel: { | |
print("Task cancelled: \(Task.isCancelled), \(Thread.currentThread)") | |
} | |
} | |
task.cancel() | |
} | |
// Task cancelled: true | |
// Job done! true | |
} | |
// слайд 55-51 | |
func sample21() { | |
class SomeService { | |
private enum Locals { | |
@TaskLocal static var userRole: String = "guest" | |
} | |
func performTask() async { | |
print("Outer Task role: \(Locals.userRole)") | |
Task { | |
print("Inner Task role: \(Locals.userRole)") | |
Locals.$userRole.withValue("admin") { | |
print("Rewritten user role: \(Locals.userRole)") | |
Task.detached { | |
print("Detached role: \(Locals.userRole)") | |
} | |
} | |
} | |
} | |
} | |
// Outer Task role: guest | |
// Inner Task role: guest | |
// Rewritten user role: admin | |
// Detached Task role: guest | |
} | |
// ### Structured Concurrency | |
// слайд 46 | |
func sample22() { | |
Task { | |
defer { print("End of the task \(Thread.current)") } | |
Task { | |
print("1 \(Thread.currentThread)") | |
} | |
Task { | |
print("2 \(Thread.currentThread)") | |
} | |
} | |
// End of the task <NSThread: 0x6000009e82c0>{number = 2, name = (null)} | |
// 2 <NSThread: 0x6000009ec480>{number = 3, name = (null)} | |
// 1 <NSThread: 0x6000009e4080>{number = 4, name = (null)} | |
} | |
// слайд 45 | |
func sample23() { | |
Task { | |
defer { print("End of the task \(Thread.current)") } | |
async let _ = Task { | |
print("1 \(Thread.currentThread)") | |
}.value | |
async let _ = Task { | |
print("2 \(Thread.currentThread)") | |
}.value | |
} | |
// 1 <NSThread: 0x600000dd8180>{number = 3, name = (null)} | |
// 2 <NSThread: 0x600000ddc4c0>{number = 2, name = (null)} | |
// End of the task <NSThread: 0x600000ddc4c0>{number = 2, name = (null)} | |
} | |
// слайд 44 | |
func sample24() { | |
func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
return [] // imagine list of sources | |
} | |
func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
return [] // imagine list of subscriptions | |
} | |
func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
// слайд 43 | |
func sample25() { | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
Task { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
async let sources = Funcs.fetchNewsSources() | |
async let subs = Funcs.fetchSubscriptions() | |
return Funcs.mixAndMatch(sources: await sources, subs: await subs) | |
} | |
// Time spend: 2.1321520805358887 | |
} | |
// слайд 42 | |
func sample26() { | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
Task { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
let sourceTask = Task { | |
await Funcs.fetchNewsSources() // 2 sec | |
} | |
let subTask = Task { | |
await Funcs.fetchSubscriptions() // 1 sec | |
} | |
return Funcs.mixAndMatch(sources: await sourceTask.value, subs: await subTask.value) | |
} | |
// Time spend: 2.131856918334961 | |
} | |
// слайд 41 | |
func sample27() { | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
Task { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
async let sources = Funcs.fetchNewsSources() | |
async let subs = Funcs.fetchSubscriptions() | |
return Funcs.mixAndMatch(sources: await sources, subs: await subs) | |
} | |
// Time spend: 2.1321520805358887 | |
} | |
// слайд 40 | |
func sample28() { | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
Task { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
async let sources = Funcs.fetchNewsSources() | |
async let subs = Funcs.fetchSubscriptions() | |
// Oops! We forgot something. | |
} | |
// Time spend: 0.00016295909881591797 | |
} | |
// слайд 39 | |
func sample29() { | |
func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of sources | |
} | |
func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of subscriptions | |
} | |
func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
// слайд 38 | |
func sample30() { | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
Task { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
async let sources = Funcs.fetchNewsSources() | |
async let subs = Funcs.fetchSubscriptions() | |
} | |
// fetchSubscriptions(), is task cancelled: true | |
// fetchNewsSources(), is task cancelled: true | |
// Time spend: 0.00013709068298339844 | |
} | |
// слайд 37 | |
func sample31() { | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
let task = Task { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
async let sources = Funcs.fetchNewsSources() | |
async let subs = Funcs.fetchSubscriptions() | |
return Funcs.mixAndMatch(sources: await sources, subs: await subs) | |
} | |
Task { | |
task.cancel() | |
} | |
// fetchSubscriptions(), is task cancelled: true | |
// fetchNewsSources(), is task cancelled: true | |
// Time spend: 0.00014603137969970703 | |
} | |
func sample31_v2() { // Дополнительный | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
let task = Task { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
let sourceTask = Task { | |
await Funcs.fetchNewsSources() // 2 sec | |
} | |
let subTask = Task { | |
await Funcs.fetchSubscriptions() // 1 sec | |
} | |
return Funcs.mixAndMatch(sources: await sourceTask.value, subs: await subTask.value) | |
} | |
Task { task.cancel() } | |
// fetchSubscriptions(), is task cancelled: false | |
// fetchNewsSources(), is task cancelled: false | |
// Time spend: 2.130429983139038 | |
} | |
// слайд 36 | |
func sample32() { | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
let task = Task(priority: .background) { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
let sourceTask = Task { | |
await Funcs.fetchNewsSources() // 2 sec | |
} | |
let subTask = Task { | |
await Funcs.fetchSubscriptions() // 1 sec | |
} | |
return Funcs.mixAndMatch(sources: await sourceTask.value, subs: await subTask.value) | |
} | |
Task(priority: .high) { | |
await task.value | |
} | |
// fetchSubscriptions(), p: TaskPriority.background | |
// fetchNewsSources(), p: TaskPriority.high | |
// Time spend: 2.1999330520629883 | |
} | |
// слайд 35 | |
func sample33() { | |
enum Funcs { | |
static func fetchNewsSources() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 2) // 2 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of sources | |
} | |
static func fetchSubscriptions() async -> [String] { | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) // 1 sec | |
print("\(#function), is task cancelled: \(Task.isCancelled)") | |
return [] // imagine list of subscriptions | |
} | |
static func mixAndMatch(sources: [String], subs: [String]) -> [String] { | |
return [] // imagine cool result | |
} | |
} | |
let task = Task(priority: .background) { | |
let date = Date() | |
defer { print("Time spend: \(date.distance(to: Date()))") } | |
async let sources = Funcs.fetchNewsSources() | |
async let subs = Funcs.fetchSubscriptions() | |
return Funcs.mixAndMatch(sources: await sources, subs: await subs) | |
} | |
Task(priority: .high) { | |
await task.value | |
} | |
// fetchSubscriptions(), p: TaskPriority.high | |
// fetchNewsSources(), p: TaskPriority.high | |
// Time spend: 2.2006049156188965 | |
} | |
// слайд 33-31 | |
func sample34() { | |
Task(priority: .background) { | |
await withTaskGroup(of: Void.self) { group in | |
for i in 0..<5 { | |
let p: TaskPriority = i % 2 == 0 ? .high : .low | |
group.addTask(priority: p) { | |
print("\(i), p: \(Task.currentPriority), base: \(String(describing: Task.basePriority))") | |
} | |
} | |
} | |
} | |
// 3, p: TaskPriority.background, base: Optional(TaskPriority.low) | |
// 4, p: TaskPriority.background, base: Optional(TaskPriority.high) | |
// 0, p: TaskPriority.background, base: Optional(TaskPriority.high) | |
// 1, p: TaskPriority.background, base: Optional(TaskPriority.low) | |
// 2, p: TaskPriority.background, base: Optional(TaskPriority.high) | |
} | |
// ### Actors | |
// слайд 28 | |
func sample35() { | |
class Counter { | |
var count: Int = 4815162342 | |
var description: String { "some old tv show reference" } | |
} | |
enum Button { case left, right, up, down, A, B, C, x, y, z } | |
let counter = Counter() | |
let buttons: [Button] = [.left, .right, .C] | |
let point = CGPoint(x: 9, y: 42) | |
let attributedText = NSAttributedString("Pretty text") | |
Task { | |
print("\(counter.count), \(buttons), \(point.x)") | |
print(attributedText) // ❌ Capture of 'attributedText' with non-sendable type 'NSAttributedString' in a `@Sendable` closure | |
} | |
} | |
// слайд 27 | |
func sample36() async { | |
class Counter: Sendable { // ❌ Non-final class 'Counter' cannot conform to 'Sendable'; use '@unchecked Sendable' | |
var count: Int = 4815162342 // ❌ Stored property 'count' of 'Sendable'-conforming class 'Counter' is mutable | |
var description: String { "some old tv show reference" } | |
} | |
enum Button: Sendable { case left, right, up, down, A, B, C, x, y, z } | |
let counter = Counter() | |
let buttons: [Button] = [.left, .right, .C] | |
let point = CGPoint(x: 9, y: 42) | |
Task { | |
print("\(counter.count), \(buttons), \(point.x)") | |
} | |
} | |
// слайд 26 | |
func sample37() async { | |
class Counter: @unchecked Sendable { | |
private(set) var count: Int = 0 | |
func increment() { | |
count += 1 | |
} | |
} | |
let counter = Counter() | |
for _ in 0..<10_000 { | |
Task { | |
counter.increment() | |
} | |
} | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
print(counter.count) | |
// 🏎️ ну, вы поняли, нестабильные 10_000 😅 | |
} | |
// слайд 25 | |
func sample38() async { | |
class Counter: @unchecked Sendable { | |
private(set) var count: Int = 0 | |
private let lock = NSLock() | |
func increment() { | |
lock.withLock { | |
count += 1 | |
} | |
} | |
} | |
let counter = Counter() | |
for _ in 0..<10_000 { | |
Task { | |
counter.increment() | |
} | |
} | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
print(counter.count) // стабильно 10000 😏 | |
} | |
// слайд 24 | |
func sample39() async { | |
actor Counter { // Sendable | |
private(set) var count: Int = 0 | |
func increment() { | |
count += 1 | |
} | |
} | |
let counter = Counter() | |
for _ in 0..<10_000 { | |
Task { | |
await counter.increment() | |
} | |
} | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
print(await counter.count) // 10000 😎 | |
} | |
// слайд 22 | |
func sample40() { | |
actor SampleActor { | |
private var title = "Im isolated to SampleActor" | |
func doSomeWork() { | |
// Выполняется на акторе, поэтому данные можно менять свободно | |
Task { | |
title = "Hello from Task!" | |
print(title) // Hello from Task! | |
await doSomeAsyncWork() | |
print(title) // Hello from async function! | |
// Выполняется на cooperative thread pool, не на акторе | |
Task.detached { [self] in | |
await self.someNonIsolatedWork() | |
} | |
} | |
} | |
func doSomeAsyncWork() async { | |
title = "Hello from async function!" | |
} | |
nonisolated func someNonIsolatedWork() async { | |
// Для обращения актору понадобится await | |
// Выполняется на cooperative thread pool, не на акторе | |
// nonisolated синхронная функция выполняется там, где её вызвали | |
print(await title) // Hello from async function! | |
} | |
} | |
} | |
// слайд 21 | |
func sample41() { | |
@globalActor actor StorageActor: GlobalActor { | |
static let shared = StorageActor() | |
} | |
// Удобно изолируем всякое разное на акторе! | |
@StorageActor final class Cache { | |
// Все внутри будет выполняться на @StorageActor | |
} | |
final class SomeStrangeStorage { | |
@StorageActor var isolatedData: Data? | |
@StorageActor func isolatedStore(data: Date) async { } | |
func doNonIsolatedWork() { } | |
// MainActor — глобальный актор, гарантирует выполнение кода на главном потоке! | |
@MainActor | |
func updateUI() { } | |
} | |
} | |
// слайд 19 | |
func sample42() { | |
actor BankAccount { | |
private var balance: Double | |
init(balance: Double) { | |
self.balance = balance | |
} | |
func withdraw(amount: Double) { | |
print("p: \(Task.currentPriority), \(Thread.currentThread)") | |
guard balance >= amount else { return } | |
balance -= amount | |
} | |
func deposit(amount: Double) { | |
print("p: \(Task.currentPriority), \(Thread.currentThread)") | |
balance += amount | |
} | |
} | |
} | |
// слайд 18 | |
func sample43() { | |
let actor = SampleActor() | |
Task { | |
print("\(#function) \(Thread.currentThread)") | |
await actor.isolatedWork() | |
} | |
Task { | |
print("\(#function) \(Thread.currentThread)") | |
actor.nonIsolatedWork() | |
} | |
//p: TaskPriority.medium, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
//p: TaskPriority.high, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
//p: TaskPriority.high, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
//p: TaskPriority.medium, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
//p: TaskPriority.medium, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
//p: TaskPriority.medium, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
//p: TaskPriority.medium, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
//p: TaskPriority.low, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
//p: TaskPriority.low, <NSThread: 0x6000004a8200>{number = 2, name = (null)} | |
} | |
// слайд 16 | |
func sample44() { | |
actor BankAccount { | |
private var balance: Double | |
init(balance: Double) { | |
self.balance = balance | |
} | |
func withdraw(amount: Double) async { | |
guard balance >= amount else { return } | |
print("Снимаем \(amount) с баланса \(balance)") | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
balance -= amount | |
print("Операция завершена: Остаток \(balance)") | |
} | |
func deposit(amount: Double) { | |
print("p: \(Task.currentPriority), \(Thread.currentThread)") | |
balance += amount | |
} | |
} | |
} | |
// слайд 15 | |
func sample45() { | |
actor BankAccount { | |
private var balance: Double | |
init(balance: Double) { | |
self.balance = balance | |
} | |
func withdraw(amount: Double) async { | |
guard balance >= amount else { return } | |
print("Снимаем \(amount) с баланса \(balance)") | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
balance -= amount | |
print("Операция завершена: Остаток \(balance)") | |
} | |
func deposit(amount: Double) { | |
print("p: \(Task.currentPriority), \(Thread.currentThread)") | |
balance += amount | |
} | |
} | |
Task { | |
let account = BankAccount(balance: 100) | |
Task { | |
await account.withdraw(amount: 100) | |
} | |
Task { | |
await account.withdraw(amount: 100) | |
} | |
} | |
// Снимаем 100.0 с баланса 100.0 | |
// Снимаем 100.0 с баланса 100.0 | |
// Операция завершена: Остаток 0.0 | |
// Операция завершена: Остаток -100.0 | |
} | |
// слайд 14 | |
func sample46() { | |
actor BankAccount { | |
private var balance: Double | |
init(balance: Double) { | |
self.balance = balance | |
} | |
func withdraw(amount: Double) async { | |
print("Снимаем \(amount) с баланса \(balance)") | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * 1) | |
guard balance >= amount else { | |
print("Отмена: нехватка средств") | |
return | |
} | |
balance -= amount | |
print("Операция завершена: Остаток \(balance)") | |
} | |
func deposit(amount: Double) { | |
print("p: \(Task.currentPriority), \(Thread.currentThread)") | |
balance += amount | |
} | |
} | |
} | |
// слайд 13 | |
func sample47() { | |
Task { | |
let account = BankAccount(balance: 100) | |
Task { | |
await account.withdraw(amount: 100) | |
} | |
Task { | |
await account.withdraw(amount: 100) | |
} | |
} | |
// Снимаем 100.0 с баланса 100.0 | |
// Снимаем 100.0 с баланса 100.0 | |
// Операция завершена: Остаток 0.0 | |
// Отмена: нехватка средств | |
} | |
// ### Swift Concurrency & GCD | |
// слайд 10 | |
func sample48() { | |
func fetchNews(completion: @escaping ([String]) -> Void) { | |
DispatchQueue.global().async { | |
completion(["Some cool news"]) | |
} | |
} | |
func fetchNews() async -> [String] { | |
// 🤔🤔🤔 А как вызвать fetchNews(completion:) ?! | |
// fetchNews { news in // ❌ если раскомментировать, будет ошибка компиляции | |
// // 🤔🤔🤔 | |
// } | |
// } | |
return [] // добавил для того, чтобы собиралось | |
} | |
} | |
// слайд 8 | |
func sample49() { | |
@Sendable func fetchNews(completion: @escaping ([String]) -> Void) { // @Sendable для Swift 6.0, тк скоуп есть искусственный | |
DispatchQueue.global().async { | |
completion(["Some cool news"]) | |
} | |
} | |
@Sendable func fetchNews() async -> [String] { // @Sendable для Swift 6.0, тк скоуп есть искусственный | |
await withCheckedContinuation { continuation in | |
fetchNews { news in | |
// ⚠️ Важно вызвать resume только 1 раз! | |
continuation.resume(returning: news) | |
} | |
// Fatal error: SWIFT TASK CONTINUATION MISUSE: | |
// fetchNews() tried to resume its continuation | |
// more than once, returning ["Some cool news"]! | |
// continuation.resume(returning: ["What if?"]) | |
} | |
} | |
Task { | |
let news = await fetchNews() | |
print(news) // ["Some cool news"] | |
} | |
} | |
// ### ??? | |
// слайд 5 | |
func sample50() { | |
enum Worker { | |
static func runRandomPriorityTask(seconds: UInt32) { | |
// доступные приоритеты: [25, 21, 17, 9] | |
let raws: [UInt8] = [25] | |
let priority = TaskPriority(rawValue: raws.randomElement()!) | |
Task(priority: priority) { | |
print("\(Thread.currentThread)") | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * UInt64(seconds)) | |
} | |
} | |
} | |
for _ in 1..<10_000 { | |
Worker.runRandomPriorityTask(seconds: 1) | |
} | |
// 1 приоритет и 1 секунда ожидания | |
//<NSThread: 0x6000008cc1c0>{number = 6, name = (null)} | |
//<NSThread: 0x6000008d8100>{number = 8, name = (null)} | |
//<NSThread: 0x6000008cc300>{number = 11, name = (null)} | |
} | |
// слайд 4 | |
func sample51() { | |
enum Worker { | |
static func runRandomPriorityTask(seconds: UInt32) { | |
// доступные приоритеты: [25, 21, 17, 9] | |
let raws: [UInt8] = [25, 21] | |
let priority = TaskPriority(rawValue: raws.randomElement()!) | |
Task(priority: priority) { | |
print("\(Thread.currentThread)") | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * UInt64(seconds)) | |
} | |
} | |
} | |
for _ in 1..<10_000 { | |
Worker.runRandomPriorityTask(seconds: 1) | |
} | |
// 2 приоритета и 1 секунда ожидания | |
//<NSThread: 0x6000037684c0>{number = 10, name = (null)} | |
//<NSThread: 0x6000037701c0>{number = 13, name = (null)} | |
//<NSThread: 0x600003778240>{number = 16, name = (null)} 🤔 | |
} | |
// слайд 3 | |
func sample52() { | |
enum Worker { | |
static func runRandomPriorityTask(seconds: UInt32) { | |
// доступные приоритеты: [25, 21, 17, 9] | |
let raws: [UInt8] = [25, 21, 17] | |
let priority = TaskPriority(rawValue: raws.randomElement()!) | |
Task(priority: priority) { | |
print("\(Thread.currentThread)") | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * UInt64(seconds)) | |
} | |
} | |
} | |
for _ in 1..<10_000 { | |
Worker.runRandomPriorityTask(seconds: 5) | |
} | |
// 3 приоритета и 5 секунд ожидания | |
//<NSThread: 0x600000c38080>{number = 2, name = (null)} | |
//<NSThread: 0x600000c3c540>{number = 14, name = (null)} | |
//<NSThread: 0x600000c20580>{number = 17, name = (null)} 🤯 | |
} | |
// слайд 2 | |
func sample53() { | |
enum Worker { | |
static func runRandomPriorityTask(seconds: UInt32) { | |
// доступные приоритеты: [25, 21, 17, 9] | |
let raws: [UInt8] = [25, 21, 17, 9] | |
let priority = TaskPriority(rawValue: raws.randomElement()!) | |
Task(priority: priority) { | |
print("\(Thread.currentThread)") | |
try? await Task.sleep(nanoseconds: NSEC_PER_SEC * UInt64(seconds)) | |
} | |
} | |
} | |
for _ in 1..<10_000 { | |
Worker.runRandomPriorityTask(seconds: 5) | |
} | |
// 4 приоритета и 5 секунд ожидания | |
//<NSThread: 0x600001544080>{number = 3, name = (null)} | |
//<NSThread: 0x600001558140>{number = 9, name = (null)} | |
//<NSThread: 0x60000157bd40>{number = 18, name = (null)} 😱 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment