28-管道与质数
12.7.2 管道与质数
使用协程可以生成一个无穷质数序列,为了方便调用方控制协程运行的位置,需要在定义时引入一个显式上下文参数context。
fun numbersFrom(context: CoroutineContext, start: Int) = produce<Int>(context) {
var x = start
while (true) send(x++)
}
定义一个接收管道数据的函数,并对接收的数字流进行质数过滤。
fun filterPrimes(context: CoroutineContext, numbers: ReceiveChannel<Int>, prime: Int) = produce<Int>(context) {
for (x in numbers) if (x % prime != 0) send(x)
}
然后定义一个测试函数,并在主线程中运行整合的管道。
fun testPrimes() = runBlocking {
var producerJob = numbersFrom(coroutineContext, 2)
//输出100个质数
for (i in 1..10) {
val prime = producerJob.receive()
println("${prime}")
producerJob = filterPrimes(coroutineContext, producerJob, prime)
}
}
运行上面的代码,输出的结果如下。
2
3
5
7
11
13
17
19
23
29
除了使用普通的协程来构建管道之外,还可以使用标准库中的buildIterator协程来构建管道。但是使用通道中的管道的好处在于,如果在CommonPool中运行协程,则可以利用CPU多内核处理的优势。
无论如何,使用上面例子中的方式来寻找质数是非常不切实际的,实际上,管道确实涉及一些其他方式的挂起调用操作(如异步调用、远程服务),而且这些管道不能使用buildSequence/buildIterator来构建,因为它们不允许有任意的挂起操作。