Быстрый параллелизм

Feb 14 2023
Введение. В Swift асинхронное программирование становится все более важным, поскольку приложения должны иметь возможность обрабатывать длительные задачи, не блокируя пользовательский интерфейс. Последняя версия Swift, Swift 5.

Введение:

В Swift асинхронное программирование становится все более важным, поскольку приложения должны иметь возможность обрабатывать длительные задачи, не блокируя пользовательский интерфейс. В последней версии Swift, Swift 5.5, появился ряд новых функций, упрощающих асинхронное программирование и делающих его более выразительным. В этой статье мы рассмотрим новый синтаксис async/await, задачи, async let, группы задач, акторы и глобальные акторы, а также то, как каждая из этих функций может помочь нам писать более эффективный, удобочитаемый и удобный для сопровождения код.

  1. Асинхронно/ожидание:

Async/await позволяет нам писать асинхронные функции, возвращающие значение, без использования обработчиков завершения. Ключевое слово async используется для пометки функции как асинхронной, а ключевое слово await используется для ожидания завершения асинхронной операции. Вот пример:

func fetchImage() async throws -> UIImage {
 let url = URL(string: "https://example.com/image.jpg")!
 let (data, response) = try await URLSession.shared.data(from: url)
 guard (response as? HTTPURLResponse)?.statusCode == 200 else {
 throw NetworkError.invalidResponse
 }
 return UIImage(data: data)!
}

2 . Задания:

В Swift 5.5 задачи — это новый примитив параллелизма, который позволяет нам представлять единицу работы, которая может выполняться одновременно с другими задачами. Задачи можно использовать для выполнения как синхронной, так и асинхронной работы, и их можно отменить, если они больше не нужны.

Задачи создаются с помощью функции Task.init и могут запускаться с помощью функции Task.run. Вот пример:

let task = Task {
    print("Hello, world!")
}
task.run()

3. Асинхронный запуск:

Async let — это новая функция в Swift 5.5, которая позволяет нам асинхронно объявлять и инициализировать переменные. Это полезно, когда нам нужно дождаться результата асинхронной операции, прежде чем мы сможем использовать переменную.

Async let используется путем добавления ключевого слова let к ключевому слову async. Вот пример:

func fetchImage() async throws -> UIImage {
    let url = URL(string: "https://example.com/image.jpg")!
    let (data, _) = try await URLSession.shared.data(from: url)
    let image async = try UIImage(data: data)
    return image
}

4. Целевая группа:

Группы задач — это новая функция в Swift 5.5, которая позволяет нам одновременно запускать несколько задач и ждать их завершения. Группы задач полезны, когда у нас есть несколько независимых задач, которые можно выполнять одновременно, и нам нужно дождаться их завершения, прежде чем продолжить.

Группы задач создаются с помощью функции TaskGroup.init, а задачи в группу можно добавлять с помощью функции TaskGroup.add. Здесь

let group = TaskGroup<Int, Error>()
for i in 1...10 {
    group.addTask {
        await Task.sleep(1_000_000_000)
        return i * 2
    }
}

do {
    let results = try await group.waitForAll()
    print(results)
} catch {
    print("An error occurred: \(error)")
}

5. Актеры:

Актеры — это новая функция в Swift 5.5, которая позволяет нам писать потокобезопасный код более декларативным способом. Актеры — это объекты, к которым одновременно может обращаться только одна задача, и они используют передачу сообщений для связи с другими актерами. Субъекты можно использовать, чтобы избежать распространенных проблем параллелизма, таких как гонки данных и взаимоблокировки.

Вот пример:

actor BankAccount {
    private var balance: Int

    init(balance: Int) {
        self.balance = balance
    }

    func deposit(amount: Int) {
        balance += amount
    }

    func withdraw(amount: Int) -> Bool {
        if amount <= balance {
            balance -= amount
            return true
        } else {
            return false
        }
    }
}

let account = BankAccount(balance: 100)
Task.detached {
    account.deposit(amount: 50)
}
Task.detached {
    if account.withdraw(amount: 75) {
        print("Withdrawal succeeded")
    } else {
        print("Withdrawal failed")
    }
}

6 . Глобальные действующие лица:

Глобальные акторы — это новая функция в Swift 5.5, которая позволяет нам писать параллельный код более декларативным способом. Глобальный актор — это актор особого типа, к которому может обращаться любая задача в системе. Глобальные акторы можно использовать для обеспечения того, чтобы доступ к определенным типам или функциям всегда был потокобезопасным, без необходимости определять пользовательский актор для каждого из них.

Вот пример:

@globalActor
struct Logger {
    static let shared = Logger()

    func log(_ message: String) {
        print(message)
    }
}

@Logger func logMessage() {
    print("Hello, world!")
}

Task.detached {
    logMessage()
}

Заключение:

Swift 5.5 представляет несколько новых функций, упрощающих написание параллельного и асинхронного кода. Async/await упрощает синтаксис для работы с асинхронными функциями, а Task и TaskGroup обеспечивают более гибкий способ управления несколькими одновременными операциями. Наконец, глобальные акторы упрощают обеспечение того, чтобы доступ к определенным типам или функциям всегда выполнялся потокобезопасным способом. Эти функции должны облегчить разработчикам написание более эффективного и надежного кода, особенно при работе со сложными параллельными системами.