注意,newSingleThreadContext创建一个新线程,这是一个非常昂贵的资源。在实际应用程序中,它必须在不再需要时释放,使用close 函数,或者存储在顶级变量中并在整个应用程序中重用。
无限制与受限制的调度员
该开敞协程调度员开始协程在调用线程,但直到第一个悬挂点。暂停后,它将在线程中恢复,该线程完全由调用的挂起函数确定。当协同程序不消耗CPU时间也不更新任何局限于特定线程的共享数据(如UI)时,无限制调度程序是合适的。
另一方面, coroutineContext 属性(在任何协同程序中可用)是对此特定协同程序的上下文的引用。这样,可以继承父上下文。特别是runBlocking协同程序的默认调度程序仅限于调用程序线程,因此继承它具有通过可预测的FIFO调度将执行限制在此线程的效果。
执行者本身执行的上下文无关紧要(正确性)。一个actor是一个协程,一个协同程序按顺序执行,因此将状态限制到特定协程可以解决共享可变状态的问题。实际上,演员可以修改自己的私有状态,但只能通过消息相互影响(避免任何锁定)。
Actor比在负载下锁定更有效,因为在这种情况下它总是有工作要做,而且根本不需要切换到不同的上下文。
注意,actor协程构建器是产品协同程序构建器的双重构件。一个actor与它接收消息的频道相关联,而一个制作者与它发送元素的频道相关联。
选择表达式可以同时等待多个挂起函数,并选择 第一个可用的挂起函数。
让我们有两个字符串生成器:fizz和buzz。该fizz生产“菲斯”串每300毫秒:
而buzz产品“Buzz!” 字符串每500毫秒:
使用接收暂停功能,我们可以接收任一从一个通道或其他。但select表达式允许我们同时使用其 onReceive子句从两者接收:
所述的onReceive条款select当信道被关闭引起相应失败 select抛出异常。我们可以使用onReceiveOrNull子句在关闭通道时执行特定操作。以下示例还显示该select表达式返回其所选子句的结果:
让我们使用它a产生“Hello”字符串四次的频道b和产生“世界”四次的频道:
这段代码的结果非常有趣,所以我们将在模式细节中分析它:
首先,select是偏向于第一条。当可以同时选择多个子句时,其中的第一个子句将被选中。在这里,两个通道都在不断地产生字符串,因此a作为select中的第一个子句的channel获胜。但是,因为我们使用的是无缓冲通道,所以a它的发送调用会不时地暂停,并且也有机会b发送。
第二个观察结果是,当通道已经关闭时,会立即选择onReceiveOrNull。
选择表达式具有onSend子句,可以与选择的偏见性结合使用。
让我们编写一个整数生成器的示例,side当主要通道上的消费者无法跟上它时,它会将其值发送到通道:
消费者将会非常缓慢,需要250毫秒才能处理每个号码:
那么让我们看看会发生什么:
可以使用onAwait子句选择延迟值。让我们从一个异步函数开始,该函数在随机延迟后返回一个延迟字符串值:
让我们随机延迟开始十几个。
现在,主函数等待第一个函数完成并计算仍处于活动状态的延迟值的数量。注意,我们在这里使用的select表达式是Kotlin DSL,因此我们可以使用任意代码为它提供子句。在这种情况下,我们遍历一个延迟值列表,onAwait为每个延迟值提供子句。
让我们编写一个使用延迟字符串值通道的通道生成器函数,等待每个接收的延迟值,但只有在下一个延迟值结束或通道关闭之前。这个例子将onReceiveOrNull和onAwait子句放在一起 select:
为了测试它,我们将使用一个简单的异步函数,它在指定的时间后解析为指定的字符串:
main函数只是启动一个协程来打印结果switchMapDeferreds并向它发送一些测试数据: