目录
4.2、指定调度器CoroutineDispatcher后四种启动模式都无法取消?
1、源码
launch协程中总共有三个参数(async的参数与launch相同),如下:
CoroutineContext 协程的上下文,EmptyCoroutineContext 表示一个空的协程上下文
CoroutineStart 协程的启动方式,它四种标准方式
suspend CoroutineScope 协程的lambda函数体
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
2、CoroutineContext
协程上下文,主要用来调度协程本身。
CoroutineContext本身是一个接口,它的继承关系:
CoroutineContext--->Element--->ContinuationInterceptor---->CoroutineDispatcher
3、CoroutineDispatcher
协程调度器,决定协程所在的线程或线程池。它可以指定协程运行于特定的一个线程、一个线程池,如果不指定任何线程(这样协程就会运行于当前线程)。
launch
函数定义如果不指定CoroutineDispatcher
或者没有其他的ContinuationInterceptor
,默认的协程调度器就是Dispatchers.Default
,Default
是一个协程调度器,其指定的线程为共有的线程池,线程数量至少为 2 最多与 CPU 数相同, 对于消耗CPU资源的计算密集型协程,这是一个合适的选择。
CoroutineDispatcher 有四种标准实现
Dispatchers.Default
、Dispatchers.IO
,Dispatchers.Main
Dispatchers.Unconfined
,Unconfined 就是不指定线程。- newSingleThreadContext 为协程的运行启动了一个线程。 一个专用的线程是一种非常昂贵的资源。 在真实的应用程序中两者都必须被释放,当不再需要的时候,使用 close 函数,或存储在一个顶层变量中使它在整个应用程序中被重用。
public object Dispatchers {
/**
* 它由JVM上的共享线程池支持。 默认情况下,使用的最大并行度为此调度程序的CPU内核数,但至少为两个。
*/
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
/**
* 协程分派器,仅限于使用UI对象操作的Main线程。可以直接使用此分派器,也可以通过[MainScope]工厂使用。
* 通常,此类调度程序是单线程的。
*/
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
/**
* 不局限于任何特定线程的协程调度程序。也就是哪个线程调用了该协程,就在该线程中运行。
* 但是只是在第一个挂起点之前是这样的,挂起恢复后运行在哪个线程完全由所调用的挂起函数决定。
* 非受限的调度器非常适用于执行不消耗 CPU 时间的任务,以及不更新局限于特定线程的任何共享数据(如UI)的协程。
* 注意:非受限的调度器是一种高级机制,可以在某些极端情况下提供帮助而不需要调度协程以便稍后执行或产生不希望的副作用,
* 因为某些操作必须立即在协程中执行。 非受限调度器不应该在通常的代码中使用。
*/
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
/**
* 这个调度器设计用于将阻塞的IO任务转移到共享线程池。将在此池中创建IO操作的线程,并根据需要将其关闭。
* 此调度程序使用的线程数受系统属性“ kotlinx.coroutines.io.parallelism”的值限制。
* 它默认为64个线程或内核数(以较大者为准)的限制。
*/
public val IO: CoroutineDispatcher = DefaultScheduler.IO
}
3.1、代码实现
fun main() {
val job1 = GlobalScope.launch(Dispatchers.Unconfined) {
println("job1所在的线程,name=${Thread.currentThread().name}")
delay(1000)
println("job1 delay后所在的线程,name=${Thread.currentThread().name}")
}
Thread(Runnable {
val block = runBlocking {
val job2 = launch(Dispatchers.Default, CoroutineStart.DEFAULT) {
println("job2所在的线程,name=${Thread.currentThread().name}")
}
val job3 = launch(Dispatchers.IO) {
println("job3所在的线程,name=${Thread.currentThread().name}")
}
val job4 = launch(Dispatchers.Unconfined) {
println("job4所在的线程,name=${Thread.currentThread().name}")
delay(1000)
println("job4 delay后所在的线程,name=${Thread.currentThread().name}")
}
val job5 = launch(newSingleThreadContext("newThread1")) {
println("job5所在的线程,name=${Thread.currentThread().name}")
}
}
}).start()
}
//输出结果
job1所在的线程,name=main
job2所在的线程,name=DefaultDispatcher-worker-1
job3所在的线程,name=DefaultDispatcher-worker-1
job4所在的线程,name=Thread-0
job5所在的线程,name=newThread1
job1 delay后所在的线程,name=kotlinx.coroutines.DefaultExecutor
job4 delay后所在的线程,name=kotlinx.coroutines.DefaultExecutor
4、CoroutineStart
协程启动的方式,协程启动选项有以下四种:
- [DEFAULT]-根据其上下文立即安排协程执行;
- [LAZY]-仅在需要时才延迟启动协程;
- [ATOMIC]-原子地(以不可取消的方式)根据协程的上下文来调度协程以使其执行;这类似于[DEFAULT],但协程不能在开始执行之前被取消。
- [UNDISPATCHED]-立即执行协程,直到其在当前线程_中的第一个挂起点_为止。就像协程已使用[Dispatchers.Unconfined]启动一样。 但是,当协程从暂停状态恢复时,它会根据其上下文中的[CoroutineDispatcher]进行调度。这与[ATOMIC]相似,即使协程已被取消,协程也开始执行,但是区别在于协程开始在同一线程中执行。
4.1、代码实现
从下面的代码看出:
- LAZY启动模式的协程,创建之后,不会立即执行,只在调用了它的start()或者join()方法的时候,才会执行。
- ATOMIC在调用了它的cancel()函数后,照样可以执行
- DEFAULT调用cancel()函数后,无法执行
- UNDISPATCHED调用了它的cancel()函数后,也可以执行
fun main() {
val block = runBlocking {
val job1 = launch(start = CoroutineStart.DEFAULT) {
println("job1执行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
}
val job2 = launch(start = CoroutineStart.LAZY) {
println("job2执行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
}
val job3 = launch(start = CoroutineStart.ATOMIC) {
println("job3执行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
}
val job4 = launch(start = CoroutineStart.UNDISPATCHED) {
println("job4执行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
}
println("执行了取消命令-----------time=${Date()}")
job1.cancel()
job3.cancel()
job4.cancel()
println("job1.isCancelled=${job1.isCancelled}")
println("job2.isCancelled=${job2.isCancelled}")
println("job3.isCancelled=${job3.isCancelled}")
println("job4.isCancelled=${job4.isCancelled}")
delay(2000)
println("启动job2的执行命令")
job2.start() //job2.start()
}
}
//输出结果
job4执行了----------time=Fri Dec 27 14:28:41 CST 2019---------------------------main
执行了取消命令-----------time=Fri Dec 27 14:28:41 CST 2019
job1.isCancelled=true
job2.isCancelled=false
job3.isCancelled=true
job4.isCancelled=false
job3执行了----------time=Fri Dec 27 14:28:41 CST 2019---------------------------main
启动job2的执行命令
job2执行了----------time=Fri Dec 27 14:28:43 CST 2019---------------------------main
4.2、指定调度器CoroutineDispatcher后四种启动模式都无法取消?
fun main() {
val block = runBlocking {
val job1 = launch(Dispatchers.Default,start = CoroutineStart.DEFAULT) {
println("job1执行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
}
val job2 = launch(Dispatchers.Default,start = CoroutineStart.LAZY) {
println("job2执行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
}
val job3 = launch(Dispatchers.Default,start = CoroutineStart.ATOMIC) {
println("job3执行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
}
val job4 = launch(Dispatchers.Default,start = CoroutineStart.UNDISPATCHED) {
println("job4执行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
}
println("执行了取消命令-----------time=${Date()}")
job1.cancel()
job3.cancel()
job4.cancel()
delay(2000)
println("启动job2的执行命令")
println("job1.isCancelled=${job1.isCancelled}")
println("job2.isCancelled=${job2.isCancelled}")
println("job3.isCancelled=${job3.isCancelled}")
println("job4.isCancelled=${job4.isCancelled}")
job2.start() //job2.start()
}
}
//输出结果
job3执行了----------time=Fri Dec 27 14:38:39 CST 2019---------------------------DefaultDispatcher-worker-2
job4执行了----------time=Fri Dec 27 14:38:39 CST 2019---------------------------main
job1执行了----------time=Fri Dec 27 14:38:39 CST 2019---------------------------DefaultDispatcher-worker-1
执行了取消命令-----------time=Fri Dec 27 14:38:39 CST 2019
启动job2的执行命令
job1.isCancelled=false
job2.isCancelled=false
job3.isCancelled=false
job4.isCancelled=false
job2执行了----------time=Fri Dec 27 14:38:41 CST 2019---------------------------DefaultDispatcher-worker-1
5、CoroutineScope
协程的第三个参数,它是一个lambda函数,也是协程需要执行的函数体部分。