我找到了包含takeWhile
(找到here)
fun <T> Sequence<T>.takeWhileInclusive(pred: (T) -> Boolean): Sequence<T> {
var shouldContinue = true
return takeWhile {
val result = shouldContinue
shouldContinue = pred(it)
result
}
}
问题是我不是100%确信如果在并行序列上使用它是安全的。
我担心的是,我们依靠shouldContinue
变量来知道何时停止,但我们并没有同步它的访问权限。
任何见解?
答案 0 :(得分:1)
这是我到目前为止所知道的。
问题不明确。没有平行序列这样的东西我可能会把它们与Java's parallel streams混在一起。我的意思是同时消耗的序列。
正如@LouisWasserman在评论中指出的那样,序列不是为并行执行而设计的。特别是SequenceBuilder
注释了@RestrictSuspension
。引自Kotlin Coroutine repo:
这意味着其范围内没有lambda的SequenceBuilder扩展可以调用suspendContinuation或其他常规挂起函数
据说@MarkoTopolnik评论说它们仍然可以像其他任何对象一样在并行程序中使用。
作为一个例子,这是第一次尝试并行使用序列
fun launchProcessor(id: Int, iterator: Iterator<Int>) = launch {
println("[${Thread.currentThread().name}] Processor #$id received ${iterator.next()}")
}
fun main(args: Array<String>) {
val s = sequenceOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
runBlocking {
val iterator = s.iterator()
repeat(10) { launchProcessor(it, iterator) }
}
}
此代码打印:
[ForkJoinPool.commonPool-worker-2]处理器#1收到1
[ForkJoinPool.commonPool-worker-1]处理器#0收到0
[ForkJoinPool.commonPool-worker-3]处理器#2收到2
[ForkJoinPool.commonPool-worker-2]处理器#3收到3
[ForkJoinPool.commonPool-worker-1]处理器#4收到3
[ForkJoinPool.commonPool-worker-3]处理器#5收到3
[ForkJoinPool.commonPool-worker-1]处理器#7收到5
[ForkJoinPool.commonPool-worker-2]处理器#6收到4
[ForkJoinPool.commonPool-worker-1]处理器#9收到7
[ForkJoinPool.commonPool-worker-3]处理器#8收到6
当然不是我们想要的。有些数字被消耗了两次。
另一方面,如果我们使用频道,我们可以这样写:
fun produceNumbers() = produce {
var x = 1 // start from 1
while (true) {
send(x++) // produce next
delay(100) // wait 0.1s
}
}
fun launchProcessor(id: Int, channel: ReceiveChannel<Int>) = launch {
channel.consumeEach {
println("[${Thread.currentThread().name}] Processor #$id received $it")
}
}
fun main(args: Array<String>) = runBlocking<Unit> {
val producer = produceNumbers()
repeat(5) { launchProcessor(it, producer) }
delay(1000)
producer.cancel() // cancel producer coroutine and thus kill them all
}
然后输出是:
[ForkJoinPool.commonPool-worker-2]处理器#0收到1
[ForkJoinPool.commonPool-worker-2]处理器#0收到2
[ForkJoinPool.commonPool-worker-1]处理器#1收到3
[ForkJoinPool.commonPool-worker-2]处理器#2收到4
[ForkJoinPool.commonPool-worker-1]处理器#3收到5
[ForkJoinPool.commonPool-worker-2]处理器#4收到6
[ForkJoinPool.commonPool-worker-2]处理器#0收到7
[ForkJoinPool.commonPool-worker-1]处理器#1收到8
[ForkJoinPool.commonPool-worker-1]处理器#2收到9
[ForkJoinPool.commonPool-worker-2]处理器#3收到10
此外,我们可以为这样的频道实施takeWhileInclusive
方法:
fun <E> ReceiveChannel<E>.takeWhileInclusive(
context: CoroutineContext = Unconfined,
predicate: suspend (E) -> Boolean
): ReceiveChannel<E> = produce(context) {
var shouldContinue = true
consumeEach {
val currentShouldContinue = shouldContinue
shouldContinue = predicate(it)
if (!currentShouldContinue) return@produce
send(it)
}
}
它按预期工作。