Учитывая, что в Kotlin существует способ записи последовательностей и смешивания императивного кода, требуется ли требуемый код в лямбда-выражении, являющийся аргументом функции sequence
, чтобы быть потокобезопасным?
Например, следующий сейф:
var x: Int = 5
fun someSequence(): Sequence<Int> = sequence {
while (true) {
x++
yield(x)
}
}
fun main(args: Array<String>) {
val seq = someSequence()
seq.take(200).forEach(::println)
}
Поскольку нет никакого врожденного параллелизма, который можно использовать при построении последовательностей, я не ожидаю проблем с порядком операций. Однако, учитывая, что sequence
реализован с помощью сопрограммы:
public fun <T> sequence(@BuilderInference block: suspend SequenceScope<T>.() -> Unit): Sequence<T> = Sequence { iterator(block) }
public fun <T> iterator(@BuilderInference block: suspend SequenceScope<T>.() -> Unit): Iterator<T> {
val iterator = SequenceBuilderIterator<T>()
iterator.nextStep = block.createCoroutineUnintercepted(receiver = iterator, completion = iterator)
return iterator
}
и сопрограммы не привязаны к конкретному потоку в общем, боюсь, кэшированные чтения. Я представляю два альтернативных сценария:
Функция sequence
проявляет особую осторожность, так что лямбда, которая генерирует следующий элемент, всегда выполняется в одном и том же потоке. Функции сопрограмм / приостановки - это детали реализации, которые временно передают поток управления потребителю последовательности. Это то, о чем @RestrictSuspension
? (С Безопасна ли эта реализация takeWhileInclusive? )
Лямбда, переданная в sequence
, должна быть поточно-ориентированной. Почему документация так молчалива по этому поводу? Также учебные пособия охватывают только очень простые варианты использования.
Пожалуйста, уточните, в чем дело и почему.