当前位置: 首页 > news >正文

网站制作公司交接网站服务器信息查询

网站制作公司交接,网站服务器信息查询,icp域名备案查询,wap手机网站建设方案Coroutine协程是kotlin实现的一种异步执行逻辑的方式,相对与传统的线程,协程更加简洁,高效,占用资源少。那协程到底是怎么实现异步的呢?线程在现在的操作系统中,线程是CPU调度的最少单元。所有的程序逻辑运…

Coroutine协程是kotlin实现的一种异步执行逻辑的方式,相对与传统的线程,协程更加简洁,高效,占用资源少。那协程到底是怎么实现异步的呢?

线程

在现在的操作系统中,线程是CPU调度的最少单元。所有的程序逻辑运行在线程之上。在Java API中, Thread是实现线程的基本类。它的内部实现是大量的 JNI 调用,因为线程的实现必须由操作系统直接提供支持。在 Android 平台上,Thread 的创建过程中,会调用 Linux API 中的 pthread_create 函数,这直接说明了 Java 层中的 Thread 和 Linux 系统级别的中的线程是一一对应的。

线程的问题是阻塞与运行两种状态之间的切换有相当大的资源开销,线程并不是一种轻量级资源,大量创建线程是对系统资源的一种消耗,而线程的阻塞调用会导致系统中存在大量因阻塞而不运行的线程,这对系统资源是一种极大的浪费。

协程

协程本质上可以认为是运行在线程上的代码块,协程提供的 挂起 操作会使协程暂停执行,而不会导致线程阻塞。而且协程是一种轻量级资源,一个应用中即使创建了上千个协程也不会造成太大的负担。

协程的是通过’suspend‘修饰符来修饰需要挂起的方法。suspend并不是Java的API,是kotlin通过编译器实现的。

CPS 变换

被 suspend 修饰符修饰的函数在编译期间会被编译器做特殊处理,这个特殊处理的第一步就是做CPS 变换。

CPS (Continuation-passing style)变换是一种编程风格,就是将控制流显式表示为continuation的一种编程风格. 简单来理解就是显式使用函数表示函数返回的后续操作。

例如:

suspend fun foo.await(): T

在编译期发生 CPS 变换之后:

fun foo.await(continuation: Continuation): Any?

CPS 变换后的函数多了一个 Continuation 类型的参数,Continuation就是续体。源码:

interface Continuation {

val context: CoroutineContext

fun resumeWith(result: Result)

}

Continuation是一个抽象的概念,简单来说它包装了协程在挂起之后应该继续执行的代码;在编译的过程中,一个完整的协程被分割切块成多个续体。在 await 函数的挂起结束以后,它会调用 continuation 参数的 resumeWith 函数,来恢复执行 await 函数后面的代码。

方法经过CPS 变换之后,返回值类型变成了 Any?,这是因为这个函数在发生变换后,除了要返回它本身的返回值,还要返回一个标记——COROUTINE_SUSPENDED,而这个返回类型事实上是返回类型 T 与 COROUTINE_SUSPENDED 的联合类型。由于Kotlin 中没有联合类型,所以只好用最泛化的类型 Any? 来表示,而 COROUTINE_SUSPENDED 是一个标记,返回它的挂起函数表示这个挂起函数会发生事实上的挂起操作。

状态机

Continuation为了直接支持挂起(即使协程在挂起点中断执行而在适当的时机在恢复)操作,编译器在编译挂起函数时会将函数体编译为状态机。主要是为了性能考虑,避免多创建类和对象。

如:

val a = a()

val y = foo(a).await() // #1

b()

val z = bar(a, y).await() // #2

c(z)

编译之后生成的伪代码:

class extends SuspendLambda<...> {

// 状态机当前状态

int label = 0

// 协程的局部变量

A a = null

Y y = null

void resumeWith(Object result) {

if (label == 0) goto L0

if (label == 1) goto L1

if (label == 2) goto L2

else throw IllegalStateException()

L0:

// 这次调用,result 应该为空

a = a()

label = 1

result = foo(a).await(this) // 'this' 作为续体传递

if (result == COROUTINE_SUSPENDED) return // 如果 await 挂起了执行则返回

L1:

// 外部代码传入 .await() 的结果恢复协程

y = (Y) result

b()

label = 2

result = bar(a, y).await(this) // 'this' 作为续体传递

if (result == COROUTINE_SUSPENDED) return // 如果 await 挂起了执行则返回

L2:

// 外部代码传入 .await() 的结果恢复协程

Z z = (Z) result

c(z)

label = -1 // 没有其他步骤了

return

}

}

一个挂起函数会被编译成一个匿名类,匿名类中的一个函数实现了这个状态机。成员变量 label 代表了当前状态机的状态,每一个续体(即挂起点中间的部分以及挂起点与函数头尾之间的部分)都各自对应了一个状态,当函数运行到每个挂起点时,label 的值都受限会发生改变,并且当前的续体(也就是代码中的this)都会作为实参传递给发生了 CPS 变换的挂起函数,如果这个挂起函数没有发生事实上的挂起,函数继续运行,如果发生了事实上的挂起,则函数直接 return。

由于 label 记录了状态,所以在协程恢复的时候,可以根据状态使用 goto 语句直接跳转至上次的挂起点并向后执行,这就是协程挂起的原理。另外,虽然 Java 中没有 goto 语句,但是 class 字节码中支持 goto。

续体拦截器

挂起函数在恢复的时候,理论上可能会在任何一个线程上恢复,有时我们需要限定协程运行在指定的线程,例如在Android中,更新 UI 的操作只能在 UI 主线程中进行。

android MainDispatcherLoader的实现:

// Main 调度器

@JvmStatic

public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher

// dispatcher 由 loadMainDispatcher() 函数创建

internal object MainDispatcherLoader {

@JvmField

val dispatcher: MainCoroutineDispatcher = loadMainDispatcher()

private fun loadMainDispatcher(): MainCoroutineDispatcher {

......

}

}

// MainCoroutineDispatcher

public abstract class MainCoroutineDispatcher : CoroutineDispatcher() {

@ExperimentalCoroutinesApi

public abstract val immediate: MainCoroutineDispatcher

}

// CoroutineDispatcher

public abstract class CoroutineDispatcher :

AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {

......

}

@InternalCoroutinesApi

public fun MainDispatcherFactory.tryCreateDispatcher(factories: List): MainCoroutineDispatcher =

try {

createDispatcher(factories)

} catch (cause: Throwable) {

MissingMainCoroutineDispatcher(cause, hintOnError())

}

/**

* @suppress

*/

@InternalCoroutinesApi

public object MissingMainCoroutineDispatcherFactory : MainDispatcherFactory {

override val loadPriority: Int

get() = -1

override fun createDispatcher(allFactories: List): MainCoroutineDispatcher {

return MissingMainCoroutineDispatcher(null)

}

}

// ContinuationInterceptor(续体拦截器)

public interface ContinuationInterceptor : CoroutineContext.Element {

companion object Key : CoroutineContext.Key

public fun interceptContinuation(continuation: Continuation): Continuation

public fun releaseInterceptedContinuation(continuation: Continuation) {

/* do nothing by default */

}

// Performance optimization for a singleton Key

public override operator fun get(key: CoroutineContext.Key): E? =

@Suppress("UNCHECKED_CAST")

if (key === Key) this as E else null

// Performance optimization to a singleton Key

public override fun minusKey(key: CoroutineContext.Key): CoroutineContext =

if (key === Key) EmptyCoroutineContext else this

}

ContinuationInterceptor,负责拦截协程在恢复后应执行的代码(即续体)并将其在指定线程或线程池恢复

在挂起函数的编译中,每个挂起函数都会被编译为一个实现了 Continuation 接口的匿名类,而续体拦截器会拦截真正挂起协程的挂起点的续体。在协程中调用挂起函数,挂起函数不一定会真正挂起协程

如:

launch {

val deferred = async {

// 异步逻辑

......

}

......

deferred.await()

......

}

在 deferred.await() 这行执行的时候,如果异步逻辑已经执行完成并取得了结果,那 await 函数会直接取得结果,而不会挂起协程。相反,如果网络请求还未产生结果,await 函数就会使协程挂起。续体拦截器只拦截真正发生挂起的挂起点后的续体,对于未发生挂起的挂起点,续体会被直接调用 resumeWith 这一类的函数而不需要续拦截器对它进行操作。除此之外,续体拦截器还会缓存拦截过的续体,并且在不再需要它的时候调用 releaseInterceptedContinuation 函数释放它。

http://www.lbrq.cn/news/2804581.html

相关文章:

  • 城市分站cms怎么做网络推广
  • 网站建设 百度推广网站优化公司哪家效果好
  • 制作网站服务公司app开发需要哪些技术
  • ppt做长图网站网络营销推广公司简介
  • 做网站图片素材在线编辑网店推广网站
  • 怎么让百度收录我的网站优化关键词软件
  • ecshop外贸网站黄页网络的推广网站有哪些
  • 门户网站建设内在百度上打广告找谁推广产品
  • 温州市网站制作哪家便宜nba西部最新排名
  • 网站运维工作内容cctv 13新闻频道
  • 网站推广途径和要点有哪些沈阳关键词自然排名
  • bootstrap 新闻网站模板网店推广实训报告
  • 多用户网站建设查网站排名
  • 5星做号宿水软件的网站广丰网站seo
  • 阳泉做网站多少钱百度天眼查
  • 网站报错500竞价什么意思
  • 巩义云启网站建设深圳网站制作公司
  • 服装租赁 网站 php杭州旺道企业服务有限公司
  • 西安个人建网站百度站长平台链接提交
  • 自己做网站的好处河池网站seo
  • 做网站建设优化的公司百度有钱花人工客服
  • 江西省赣州市崇义县搜狗整站优化
  • 企业网站建设的一般要素主要包括网站的seo资料站
  • 黑龙江头条今日新闻郑州好的seo外包公司
  • 社群营销的具体方法放心网站推广优化咨询
  • 可靠的网站建设案例社群营销策略有哪些
  • 网页源代码看答案怎么优化推广自己的网站
  • 网站备案登录密码找回北大青鸟培训机构靠谱吗
  • 360 网站备案百度收录哪些平台比较好
  • a站和b站的区别熊猫关键词工具官网
  • Android 16环境开发的一些记录
  • 效率跃迁 ,亚数TrustAsia 加速证书管理迈向 CaaS 新阶段
  • 两台电脑之间如何传输大文件?
  • QT QProcess, WinExec, ShellExecute中文路径带空格程序或者脚本执行并带参数
  • 数据结构之排序大全(4)
  • 缓存与Redis