Kotlin协程 — 子协程中无法进行IO、网络请求等并发

⼦协程 :
当⼀个协程被其它协程在 CoroutineScope 中启动的时候,它将通过CoroutineScope.coroutineContext 来承袭上下⽂,并且这个新协程的 Job 将会成为⽗协程作业的⼦作业。当⼀个⽗协程被取消的时候,所有它的⼦协程也会被递归的取消。
然⽽,当 GlobalScope 被⽤来启动⼀个协程时(顶层协程),它与作⽤域⽆关且是独⽴被启动的协程 。

下面有个例子用来测试子协程中是否可以进行网络请求并发:
Kotlin协程 — 子协程中无法进行IO、网络请求等并发_第1张图片
上图中的两个http页面返回时间都在服务器设置了为固定的2秒,也就是说如果子协程如果支持并发的话,总耗时应该是2秒多一点,运行结果是4秒多,很显然子协程中并不支持网络请求的并发。下面将用顶层协程来重新测试:
Kotlin协程 — 子协程中无法进行IO、网络请求等并发_第2张图片
从结果中可以看出使用顶层协程是可以进行网络请求并发的,那为什么子协程中无法进行并发呢?原因是所有的子协程都是跟父协程共享一条线程,当子协程中有挂起函数被调用后,共享的线程就会切换到到别的子协程或父协程中去处理需要处理的事务,但是网络请求不是挂起函数,挂起函数不会阻塞线程,网络请求是会阻塞线程的。拿上面的第一个例子来说,第一个协程在进行网络请求的时候线程被阻塞了,只有等这个网络请求之后,共享线程才可以切换到第二个子协程中去进行网络请求,所以就无法进行并行请求了。究其原因就是网络请求是阻塞线程的,而所有子协程都是与父协程都是共享一条线程,所以会阻塞线程的操作都无法在子协程中进行并发,比如:网络请求、IO等操作。

顶层协程可以并发的原因:顶层协程跟子协程不一样,所有顶层协程使用的不止一条线程,而是可以随机从线程池中随机抽取一条线程来完成业务,线程池中有N条线程,当顶层协程调用挂起函数时,这个协程会挂起,而线程会回到线程池中,当挂起函数恢复时会在线程池中随机抽取一条线程来使用。启动1000条顶层协程只需要6、7条线程,其实这个线程数量不是由用户来控制,是由kotlin底层控制的,根据业务需求启动的线程数量不固定,也就是说当业务所需的线程数量不足时会自动调整线程池中的线程数量。

为了更好理解子协程和父协程,打个比喻:一个人兼职几份工作,分别是上午、下午、晚上的工作,这个人就是线程,而这三份工作就是启动的三条协程,当其他两份工作不上班的时候就去那个需要上班的地方工作(当其他两个协程调用挂起函数的时候,这个线程就去那个需要处理业务的协程中处理业务),一个人兼职三份工作很符合所有子协程与父协程共享一条线程的场景。 如果把两个人换成兼职10份工作,这个场景就是顶层协程共享一个线程池的场景了。 这也就是为什么协程要比线程更省资源的主要原因。

顶层协程工作时使用线程池中线程的例子:(顶层协程才能实现真正的并发,也就是异步执行)
Kotlin协程 — 子协程中无法进行IO、网络请求等并发_第3张图片
子协程和父协程共享一条线程的例子:(其实子协程中的并发就是线程同步执行多个子协程的代码,来回切换线程来达到并发的效果)
Kotlin协程 — 子协程中无法进行IO、网络请求等并发_第4张图片

CoroutineDispatcher 调度器。

如果要在子协程中实现IO并发,则要使用调度器。协程上下⽂包括了⼀个 协程调度器( CoroutineDispatcher),它确定了相应的协程在执⾏时使⽤⼀个或多个线程。协程调度器可以将协程的执⾏局限在指定的线程中,调度它运⾏在线程池或让它不受限的运⾏。
所有的协程构建器诸如 launch 和 async 接收⼀个可选的 CoroutineContext 参数,它可以被⽤来显式的为⼀个新协程或其它上下⽂元素指定⼀个调度器。

下面的例子就是使用调度器来让子协程中进行网络请求拥有并发的能力,withContext函数功能:使用给定的协程上下文调用指定的挂起块,挂起到完成为止,然后返回结果。它的参数Dispatchers.IO就是一个调度器(public val IO: CoroutineDispatcher = DefaultScheduler.IO 这是IO属性在源码中的定义),CoroutineDispatcher用于将阻塞IO任务卸载到共享线程池,这个时候withContext函数里面的代码跟运行在顶层协程里的没区别了,都是使用着这个共享线程池,所以也就有了真正并发的能力。
Kotlin协程 — 子协程中无法进行IO、网络请求等并发_第5张图片
从上图可以看到两个子协程withContext函数里的线程不再使用跟父协程共享的线程,而是使用了共享线程池,每个子协程的withContext函数里的代码都运行在了一条单独的线程中,这就是并发了(异步)。

你可能感兴趣的:(Kotlin)