Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Noluk1991/09c59a3339e81984d1b15190619869d4 to your computer and use it in GitHub Desktop.
Save Noluk1991/09c59a3339e81984d1b15190619869d4 to your computer and use it in GitHub Desktop.
Code samples из презентации
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