Это заметки с сессии 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()
}
}
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 }
}
После просмотра этой части я не могу понять, почему актер — не лучший вариант. Пожалуйста, пролейте свет на это.





Вы включаете один из комментариев, объясняющий, почему государство не может быть актором:
мы не можем гарантировать порядок выполнения операций над актером, поэтому мы не можем гарантировать, что наша отмена будет выполнена первой.
По этой причине актер не очень подходит для синхронизации конечного автомата. Когда вы отменяете последовательность, вы хотите быть уверены, что отмена будет выполнена первой.
Для тех, кто не знаком с поведением актеров, не относящихся к FIFO, я бы отослал вас к SE-0306 – Актеры, в котором говорится:
Последовательный исполнитель по умолчанию отвечает за поочередное выполнение частичных задач. Концептуально это похоже на сериал
DispatchQueue, но с важным отличием: задачи, ожидающие актера, не обязательно будут выполняться в том же порядке, в котором они изначально ожидали этого актера. … В отличие от сериаловDispatchQueue, в которых действует принцип «первым пришел — первым вышел».
Кроме того, это видео продолжает объяснять вторую проблему (выделено автором):
Мы делаем это, синхронно вызывая функцию
cancelв нашем конечном автомате последовательности. … Эти механизмы [блокировка, вызов синхронизации с последовательной очередью GCD или атомарность] позволяют нам синхронизировать общее состояние, избегая условий гонки, и в то же время позволяя нам отменить работающий конечный автомат без введения неструктурированной задачи в обработчик отмены.
Они предлагают, чтобы синхронизация государственного объекта была синхронной. Они советуют не запускать неструктурированную задачу по обновлению конечного автомата (что вам пришлось бы делать, если бы это был актер).
Короче говоря, когда вы отменяете последовательность, вы действительно хотите быть уверены, что больше элементов быть не может, что означает синхронную синхронизацию, т. е. не актера, а что-то вроде блокировки, атомарной или последовательной очереди GCD.
Для полноты картины привожу всю цитату из этого видео:
Как и в случае с синхронными итераторами, функция
nextвозвращает следующий элемент последовательности илиnil, чтобы указать, что мы находимся в конце последовательность. Многие AsyncSequence реализованы с состоянием. машина, которую мы используем для остановки выполняемой последовательности.В нашем примере, когда
isRunningистинно, последовательность должна продолжать отправлять заказы. Как только задача будет отменена, нам нужно указывают, что последовательность завершена и ее следует завершить.Мы делаем это, синхронно вызывая функцию
cancelна нашем Конечный автомат последовательности.Обратите внимание: поскольку обработчик отмены запускается немедленно, состояние машина имеет общее изменяемое состояние между обработчиком отмены и основной корпус, который может работать одновременно. Нам нужно будет защитить наше государство машина. Хотя актеры отлично подходят для защиты инкапсулированного состояния, мы хотите изменить и прочитать отдельные свойства нашего конечного автомата, поэтому актеры не совсем подходящий инструмент для этого.
Более того, мы не можем гарантировать порядок выполнения операций на актер, поэтому мы не можем гарантировать, что наша отмена будет выполнена первой. Хорошо нужно что-то еще. Я решил использовать атомику из Swift Atomics пакет, но мы могли бы использовать очередь отправки или блокировки.
Эти механизмы позволяют нам синхронизировать общее состояние, избегая условия гонки, позволяя нам отменить работающий конечный автомат без введения неструктурированной задачи в обработчик отмены.
В свой ответ я добавил ссылку на SE-0306, где отмечается такое поведение…
Спасибо, Роб. Думаю, это то, что я пропустил. Актер не может гарантировать порядок выполняемых на нем операций. Это где-то упоминается официально?