GlobalScope.launch {
semaphore.withPermit {
counter ++
}
}
}
Thread.sleep(500) //暂停一会儿等待所有协程执行结束
println(“The final count is $counter”)
}
注意:只有在
permits = 1
时才和Mutex
功能相同。
源码分析
suspend
我们来看 suspend
修饰函数和修饰 lambda
的区别。
挂起函数:
suspend fun suspendFun() {
}
编译成 java
代码如下:
@Nullable
public final Object suspendFun(@NotNull Continuation $completion) {
return Unit.INSTANCE;
}
可以看到挂起函数其实隐藏着一个 Continuation
协程实例参数,而这个参数其实就来源于协程体或者其他挂起函数,因此挂起函数只能在协程体内或其他函数内调用了。
suspend
修饰 lambda
表达式:
suspend {}
// 反编译结果如下
Function1 var2 = (Function1)(new Function1((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
switch(this.label) {
case 0:
return Unit.INSTANCE;
default:
}
}
@NotNull
public final Continuation create(@NotNull Continuation completion) {
Function1 var2 = new (completion);
return var2;
}
public final Object invoke(Object var1) {
return (()this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);
}
});
suspend lambda
实际会被编译成 SuspendLambda
的子类。suspendLambda
的继承关系如下图:
通过反编译的代码可以发现我们在协程体内编写的代码最终是在 invokeSuspend
函数内执行的。而在 BaseContinuationImpl
内实现了 Continuation
协程接口的 resumeWidth
函数,并在其内调用了 invokeSuspend
函数。
suspend
关键字的介绍先到这里,接下来我们看协程是如何创建并运行的。
协程是如何被创建的
文件地址
kotlin.coroutines.Continuation.kt
。
Continuation.kt
文件基本属于协程的基础核心了,搞懂了它也就相当于搞懂了协程的基础原理。
-
协程接口的定义;
-
唤醒或启动协程的函数;
-
四种创建协程的函数;
-
帮助获取协程内的协程实例对象的函数。
首先是协程的接口声明,非常简单:
/**
- 协程接口,T 表示在最后一个挂起点恢复时的返回值类型
*/
public interface Continuation {
/**
- 协程上下文
*/
public val context: CoroutineContext
/**
- 这个函数的功能有很多,它可以启动协程,也可以恢复挂点,还可以作为最后一次挂起点恢复时输出协程的结果
*/
public fun resumeWith(result: Result)
}
协程接口声明之后 Continuation.kt
文件提供了两个调用 resumeWith
函数的函数:
public inline fun Continuation.resume(value: T): Unit =
resumeWith(Result.success(value))
public inline fun Continuation.resumeWithException(exception: Throwable): Unit =
resumeWith(Result.failure(exception))
这两个函数除了传参一成功一失败,它们的功能是一模一样的,都是直接调用了 resumeWith
函数。相当于是 resumeWith
函数的封装。
再然后就是四种创建协程的方式了:
public fun (suspend () -> T).createCoroutine(
completion: Continuation
): Continuation =
SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
public fun <R, T> (suspend R.() -> T).createCoroutine(
receiver: R,
completion: Continuation
): Continuation =
SafeContinuation(createCoroutineUnintercepted(receiver, completion).intercepted(), COROUTINE_SUSPENDED)
public fun (suspend () -> T).startCoroutine(
completion: Continuation
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}
public fun <R, T> (suspend R.() -> T).startCoroutine(
receiver: R,
completion: Continuation
) {
createCoroutineUnintercepted(receiver, completion).intercepted().resume(Unit)
}
这四种方式可以说是相似度超高,createCoroutine
和 startCoroutine
最大的区别在于,通过 createCoroutine
创建的协程需要掉用 resume
函数启动,而 startCoroutine
函数内部已经默认调用了 resume
函数。那我们先用第一种方式创建一个协程:
// 创建协程
val continuation = suspend {
println(“In Coroutine”)
}.createCoroutine(object : Continuation {
override fun resumeWith(result: Result) {
println(result)
}
override val context = EmptyCoroutineContext
})
// 启动协程
continuation.resume(Unit)
调用 createCoroutine
函数创建协程时传入了 Continuation
协程的匿名类对象,诶?好像有点不对,为什么创建协程的时候要传一个协程实例进去,直接用不就成了。想知道为什么的话,那就需要看看 createCoroutine
到底做了什么操作了。
SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
首先调用的是 createCoroutineUnintercepted
函数,它的源码可以在 kotlin.coroutines.intrinsics.IntrinsicsJvm.kt
内找到:
public actual fun (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation
): Continuation {
val probeCompletion = probeCoroutineCreated(completion)
return if (thi