Почему нельзя использовать актера в качестве типа конечного автомата в AsyncSequence?

Это заметки с сессии WWDC 2023 Beyond the basics of structured concurrency.

Многие AsyncSequences реализованы с помощью конечного автомата, который мы используем для остановки выполняемой последовательности.

public func next() async -> Order? {
    return await withTaskCancellationHandler {
        let result = await kitchen.generateOrder()
        guard state.isRunning else {
            return nil
        }
        return result
    } onCancel: {
        state.cancel()
    }
}

  • Хотя акторы отлично подходят для защиты инкапсулированного состояния, они не могут по-настоящему защитить конечный автомат.
  • Изменение и чтение отдельных свойств акторов конечного автомата — не совсем подходящий инструмент.
  • Мы не можем гарантировать порядок выполнения операций над актером, поэтому мы не можем гарантировать, что наша отмена будет выполнена первой.
  • Вместо этого используйте атомику из пакета Swift Atomics, очередь отправки или блокировки.
private final class OrderState: Sendable {
    let protectedIsRunning = ManagedAtomic<Bool>(true)
    var isRunning: Bool {
        get { protectedIsRunning.load(ordering: .acquiring) }
        set { protectedIsRunning.store(newValue, ordering: .relaxed) }
    }
    func cancel() { isRunning = false }
}

После просмотра этой части я не могу понять, почему актер — не лучший вариант. Пожалуйста, пролейте свет на это.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
101
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы включаете один из комментариев, объясняющий, почему государство не может быть актором:

мы не можем гарантировать порядок выполнения операций над актером, поэтому мы не можем гарантировать, что наша отмена будет выполнена первой.

По этой причине актер не очень подходит для синхронизации конечного автомата. Когда вы отменяете последовательность, вы хотите быть уверены, что отмена будет выполнена первой.

Для тех, кто не знаком с поведением актеров, не относящихся к FIFO, я бы отослал вас к SE-0306 – Актеры, в котором говорится:

Последовательный исполнитель по умолчанию отвечает за поочередное выполнение частичных задач. Концептуально это похоже на сериал DispatchQueue, но с важным отличием: задачи, ожидающие актера, не обязательно будут выполняться в том же порядке, в котором они изначально ожидали этого актера. … В отличие от сериалов DispatchQueue, в которых действует принцип «первым пришел — первым вышел».

Кроме того, это видео продолжает объяснять вторую проблему (выделено автором):

Мы делаем это, синхронно вызывая функцию cancel в нашем конечном автомате последовательности. … Эти механизмы [блокировка, вызов синхронизации с последовательной очередью GCD или атомарность] позволяют нам синхронизировать общее состояние, избегая условий гонки, и в то же время позволяя нам отменить работающий конечный автомат без введения неструктурированной задачи в обработчик отмены.

Они предлагают, чтобы синхронизация государственного объекта была синхронной. Они советуют не запускать неструктурированную задачу по обновлению конечного автомата (что вам пришлось бы делать, если бы это был актер).

Короче говоря, когда вы отменяете последовательность, вы действительно хотите быть уверены, что больше элементов быть не может, что означает синхронную синхронизацию, т. е. не актера, а что-то вроде блокировки, атомарной или последовательной очереди GCD.


Для полноты картины привожу всю цитату из этого видео:

Как и в случае с синхронными итераторами, функция next возвращает следующий элемент последовательности или nil, чтобы указать, что мы находимся в конце последовательность. Многие AsyncSequence реализованы с состоянием. машина, которую мы используем для остановки выполняемой последовательности.

В нашем примере, когда isRunning истинно, последовательность должна продолжать отправлять заказы. Как только задача будет отменена, нам нужно указывают, что последовательность завершена и ее следует завершить.

Мы делаем это, синхронно вызывая функцию cancel на нашем Конечный автомат последовательности.

Обратите внимание: поскольку обработчик отмены запускается немедленно, состояние машина имеет общее изменяемое состояние между обработчиком отмены и основной корпус, который может работать одновременно. Нам нужно будет защитить наше государство машина. Хотя актеры отлично подходят для защиты инкапсулированного состояния, мы хотите изменить и прочитать отдельные свойства нашего конечного автомата, поэтому актеры не совсем подходящий инструмент для этого.

Более того, мы не можем гарантировать порядок выполнения операций на актер, поэтому мы не можем гарантировать, что наша отмена будет выполнена первой. Хорошо нужно что-то еще. Я решил использовать атомику из Swift Atomics пакет, но мы могли бы использовать очередь отправки или блокировки.

Эти механизмы позволяют нам синхронизировать общее состояние, избегая условия гонки, позволяя нам отменить работающий конечный автомат без введения неструктурированной задачи в обработчик отмены.

Спасибо, Роб. Думаю, это то, что я пропустил. Актер не может гарантировать порядок выполняемых на нем операций. Это где-то упоминается официально?

Alex 12.04.2024 02:00

В свой ответ я добавил ссылку на SE-0306, где отмечается такое поведение…

Rob 12.04.2024 04:44

Другие вопросы по теме