React Native中Kotlin协程delay可并发但计算任务串行问题
问题描述
我有一个React Native应用,其中JS部分调用原生Kotlin加密方法。我尝试使用Kotlin协程实现并发处理,场景为多个加密任务同时调用原生加密,每个任务计算耗时1秒。
当仅执行计算任务时,代码实现如下:
suspend fun decryptData(<params>): String { // computational task .... return string; } @ReactMethod fun decrypt(<params>) = runBlocking<Unit> { val time = measureTimeMillis { val op1 = async { decryptData(<params>) } val op2 = async { decryptData(<params>) } val op3 = async { decryptData(<params>) } val op4 = async { decryptData(<params>) } val op5 = async { decryptData(<params>) } promise.resolve("${op1.await()}, ${op2.await()}, ${op3.await()}, ${op4.await()}, ${op5.await()}") } println("Completed in $time ms") }
此时总耗时约5秒,任务为串行执行。
当在计算任务中加入3秒delay时,代码如下:
suspend fun decryptData(<params>): String { // computational task .... delay(3000) return string; }
此时总耗时约3秒,任务可并发执行。
若delay为串行执行,总耗时应额外增加3*5=15秒,但协程中的delay是并发的;然而实际计算任务却始终是串行执行的,这一现象该如何解决?
问题原因与解决方法
原因分析
Kotlin协程的并发依赖于挂起函数释放线程,但你的decryptData里的计算任务是CPU密集型的阻塞操作,没有调用任何挂起函数(比如delay),导致协程无法主动让出线程。而runBlocking默认使用单线程上下文,所有协程任务都在这同一个线程里执行,自然只能串行处理。
当加入delay后,delay作为挂起函数会释放当前线程,让其他协程有机会抢占执行,所以多个任务能并发运行。
解决方法
要让CPU密集型任务并发执行,需要把它们放到多线程的协程上下文中,比如Dispatchers.Default(专门处理CPU密集型任务,会使用线程池)。有两种修改方式:
- 在
async启动时指定上下文
@ReactMethod fun decrypt(<params>) = runBlocking<Unit> { val time = measureTimeMillis { val op1 = async(Dispatchers.Default) { decryptData(<params>) } val op2 = async(Dispatchers.Default) { decryptData(<params>) } val op3 = async(Dispatchers.Default) { decryptData(<params>) } val op4 = async(Dispatchers.Default) { decryptData(<params>) } val op5 = async(Dispatchers.Default) { decryptData(<params>) } promise.resolve("${op1.await()}, ${op2.await()}, ${op3.await()}, ${op4.await()}, ${op5.await()}") } println("Completed in $time ms") }
- 在
decryptData函数内部切换上下文
suspend fun decryptData(<params>): String = withContext(Dispatchers.Default) { // computational task .... return@withContext string; }
修改后,每个加密任务会被分配到线程池的不同线程中执行,总耗时会接近1秒(5个1秒的CPU任务并发处理)。另外注意:runBlocking本身仍在调用它的线程(React Native原生线程)执行,但async里的任务会跑在Dispatchers.Default的线程池,不会阻塞原线程。
内容的提问来源于stack exchange,提问作者Kim Mỹ
相关产品推荐
相关产品推荐

