关于协程使用归纳

基本知识

google协程系列

概念解读

设计目的: 协程通过将复杂性放入库来简化异步编程,也就是用同步编程的方式来实现原有的异步编程。

常规函数是无法调用协程相关的特性,需要启动协程后使用。

协程依赖于线程,但是协程挂起时不需要阻塞线程

疑问:为什么不会阻塞线程?
答:参考非阻塞式挂起。

非阻塞式挂起

其本质就是切到其他线程,这样原来的线程就不阻塞了。
而给我们挂起后又执行,是底层帮我们当协程执行完成后,切换回原先线程。

基本使用

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"

//可选,部分api必须,如MainScope
 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0'

三种启动方式

1.runBlocking:T
会阻断当前线程,直到该协程执行结束。通常只用于启动最外层的协程,例如线程环境切换到协程环境。

2.launch:Job
该方式启动的协程任务是不会阻塞线程的,常用,一般适用不需要返回结果的。

3.async/await:Deferred
async定义异步job,await使用异步返回的结果。注意在async代码块中的异常,默认是抛弃的,需要通过await获取。

作用域,CoroutineScope

CoroutineScope是自行传播的

创建方式

val scope = CoroutineScope(Job() + Dispatchers.Main)

结构化并发(解决多协程并发的问题)

结构化并发的作用:
结构化并发能够保证当某个作用域被取消后,它内部所创建的所有协程也都被取消.

结构化并发保证了当 suspend 函数返回时,它所处理任务也都已完成。

通俗的理解:
通过作用域实现统一的协程执行策略(是一起取消还是互不干扰,又或者与上级协程作用域无关)。

如何做
使用coroutineScope或者
supervisorScope来启动新协程。

可以解决的问题:

  • 解决协程可能出现的泄漏问题。
  • 使用作用域追踪,取消协程。
  • 按照规定启动协程(官方已有)。

GlobeScope

自成一派:在全局的作用域中执行协程。并不受当前作用域影响。(默认作用域原则)

coroutineScope.launch

coroutineScope需要内部的协程完毕后才结束,在此期间是调用方是处于挂起状态。内部协程必须全部执行完成,有一个异常,则全部取消。

suspend fun fetchTwoDocs() {
    coroutineScope {
        launch { fetchDoc(1) }
        async { fetchDoc(2) }
    }
}

supervisorScope

内部的子协程独立运行,互不干挠。执行期间调用方是挂起状态。
但子协程内的子协程依旧按默认作用域原则(这一项验证不通过,待处理)。

MainScope.launch

启动主线程中的协程,在普通非ui项目中,是不能使用。不过async方式可以用(即使不引入附属包)。
注意需要引入对应的android包。

viewModelScope

mvvm架构中的。

SupervisorJob

出异常的自己处理,不传递给父级,不影响其他协程。
val uiScope = CoroutineScope(SupervisorJob())

关键字解读

suspend

挂起关键字,挂起依靠内部协程逻辑,不能被常规函数调用。

resume

Job

join(),会等待自己以及所有子job的执行

Dispatcher

协程所在线程。如io。main等。

运用场景

并发

疑问:那这样kotlin的原生并发就没用了?

多层回调

官方使用参考

view的生命周期

viewModelScope

异常处理

归纳:
官方指导

如果异常没有被处理,而且 CoroutineContext 没有一个 CoroutineExceptionHandler (稍后讲到) 时,异常会到达默认线程的 ExceptionHandler。在 JVM 中,异常会被打印在控制台;而在 Android 中,无论异常在那个 Dispatcher 中发生,都会导致您的应用崩溃。

未被捕获的异常一定会被抛出,无论您使用的是哪种 Job。

launch

scope.launch {
    try {
        codeThatCanThrowExceptions()
    } catch(e: Exception) {
        // 处理异常
    }
}

async

当 async 被用作根协程 (CoroutineScope 实例或 supervisorScope 的直接子协程) 时不会自动抛出异常,而是在您调用 .await() 时才会抛出异常。

supervisorScope {
    val deferred = async {
        codeThatCanThrowExceptions()
    }
 
   try {
       deferred.await()
   } catch(e: Exception) {
       // 处理 async 中抛出的异常
   }
}

当 async 被用作根协程时,异常将会在您调用 .await 方法时被抛出

异常捕获

  val handler = CoroutineExceptionHandler { context, exception ->
            println("Caught $exception")
        }

        val uiScope = CoroutineScope(SupervisorJob())

        uiScope.launch(handler) {
            loadScope()
            println("执行完毕")
        }
        
    suspend fun loadScope() {
        supervisorScope {
            launch {
                delay(3000)
                println("执行编号11")

            }

            launch {
                delay(1000)
                println("执行编号12")

                launch {
                    delay(3000)
                    println("执行编号15")
                }

                throw StructuredConcurrencyWill("throw")

            }
        }
    }

思考:结合这些特性,可以考虑对有需要捕获异常的地址,就地解决,不要带到外面来,防止不能正常的捕获。

自定义线程池

coroutineDispatcher为你定义的线程池
参考:https://www.kagura.me/dev/20190728214922.html

  async(coroutineDispatcher) {
            Thread.sleep(1000)
            println("${Thread.currentThread().name}")
        }            

缺点

  • 需要关注泄漏的问题,泄漏协程会浪费内存、CPU、磁盘资源,甚至发送一个无用的网络请求

  • 在多线程数据共享或线程间数据精密控制不如java多线程更好处理。(待举证)

优点

  • 可以实现用同步的方式写异步程序
  • 可以更优雅的实现并发(协同)开发。
    协程(Coroutine)就是协同程序,而Kotlin协程就是一个基于Java Thread API封装的工具包,帮助我们轻松的写出复杂的并发代码。其底层也是对线程池进行了包装处理(所以直接性能上,对比线程池没有变化)

迷惑代码

//给您下面一段代码,您能指出 Child 1 是用哪种 Job 作为父级的吗?
val scope = CoroutineScope(Job())

scope.launch(SupervisorJob()) {
    // new coroutine -> can suspend
   launch {
        // Child 1
    }
    launch {
        // Child 2
    }
}

答案:Job,
原因:scope.launch并不能重置构造方法传入的job。所以子协程依旧是job。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity中的协程使用分为两部分:协程本体和协程调度器。协程本体是使用C#的迭代器函数来实现的,它允许函数在中间暂停并返回。而协程调度器则利用了MonoBehaviour中的生命周期函数来实现分时调度。\[1\]\[2\] 在Unity中,协程使用有多种场景。首先,它可以用于异步加载资源,通过在协程中加载资源,可以避免阻塞主线程。其次,协程可以将一个复杂的程序分帧执行,使得程序的执行更加平滑。最后,协程还可以用作定时器,通过在协程中设置等待时间来实现定时功能。\[3\] 在使用协程时,需要注意一些事项。首先,协程只能在继承了MonoBehaviour的类中使用。其次,协程函数必须返回IEnumerator类型。另外,协程函数中可以使用yield语句来暂停执行,并指定下一次执行的时间或条件。最后,协程函数可以通过StopCoroutine函数来停止协程的执行。\[3\] 总结来说,Unity的协程使用是通过协程本体和协程调度器来实现的,协程本体使用C#的迭代器函数,协程调度器利用了MonoBehaviour的生命周期函数。协程使用场景包括异步加载资源、分帧执行复杂程序和定时器。在使用协程时需要注意一些事项,如只能在继承了MonoBehaviour的类中使用协程函数必须返回IEnumerator类型等。 #### 引用[.reference_title] - *1* *3* [【Unity】Unity协程(Coroutine)的原理与应用](https://blog.csdn.net/hafeiyangha/article/details/125365152)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Unity的协程详解](https://blog.csdn.net/vinkey_st/article/details/126759402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值