```
-
coroutineScope
实现结构化并发。async
时CoroutineScope
的扩展函数,因此可以直接用coroutineScope
抽取。fun main() = runBlocking { val time = measureTimeMillis { println("The answer is ${concurrentSum()}") } println("Completed in $time ms") } suspend fun concurrentSum() = coroutineScope { val one = async { doOne() } val two = async { doTwo() } one.await() + two.await() } suspend fun doOne(): Int { delay(1000L) // 假设我们在这里做了一些有用的事 return 13 } suspend fun doTwo(): Int { delay(1000L) // 假设我们在这里做了一些有用的事 return 29 }
调度器
- 协程调度器确定了哪些线程或与线程相对应的协程执⾏,将协程限制在⼀个特定的线程执⾏,或将它分派到⼀个线程池,亦或是让它不受限地运⾏。
-
launch { }
不传参启动了承袭的上下⽂调度器, -
Dispatchers.Unconfined
是⾮受限调度器,调⽤时启动了⼀个协程,恢复线程中的协程由被调⽤的挂起函数来决定 -
Dispatchers.Default
同GlobalScope.launch {}
,⽤共享的后台线程池 -
ExecutorCoroutineDispatcher
启动了⼀个新的线程fun main() = runBlocking<Unit> { launch { print("main runBlocking I'm working in thread ${Thread.currentThread().name} \n") } launch(Dispatchers.Unconfined) { print("Unconfined I'm working in thread ${Thread.currentThread().name} \n") } launch(Dispatchers.Default) { print("Default I'm working in thread ${Thread.currentThread().name} \n") } launch(newSingleThreadContext("MyOwnThread")) { print("newSingleThreadContext I'm working in thread ${Thread.currentThread().name} \n") } }
-
流 Flow
-
Flow 类似
Java8
当中的Stream
,Flow
对异步支持更加友好 -
流使用
emit
函数 发射 值,使用collect
函数收集值,函数不再标有suspend
修饰符fun simple(): Flow<Int> = flow { for (i in 1..3) { delay(1000) emit(i) } } fun main() = runBlocking<Unit> { launch { for (k in 1..3) { println("I'm not blocked $k") delay(1000) } } simple().collect { value -> println(value) } }
-
创建流
-
flow {}
构造器fun simple(): Flow<Int> = flow { for (i in 1..3) { delay(1000) emit(i) } }
-
flowOf
构建器定义了一个发射固定值集的流fun simple(): Flow<Int> = flowOf(1, 2, 3)
-
使用
.asFlow()
扩展函数,可以将各种集合与序列转换为流(1..3).asFlow().collect { value -> println(value) }
-
常用函数
map
、filter
、transform
、take
-
末端操作
collect
、toList
、toSet
、first
、single
、reduce
、flod
-
其他函数
flowOn()更改上下文
、buffer()缓冲
、conflate()合并
-
通道 Channel
-
通道提供了一种在流中传输值的方法
-
Channel
提供挂起的send
和receive
@Test fun test_channel() = runBlocking { val channel = Channel<Int>() launch { // this might be heavy CPU-consuming computation or async logic, we'll just send five squares for (x in 1..5) channel.send(x * x) } // here we print five received integers: repeat(5) { println(channel.receive()) } println("Done!") }
-
Channel
可以保证所有先前发送出去的元素都在通道关闭前被接收到 -
Channel
实现自SendChannel
和ReceiveChannel
异常处理
-
协程构建器有两种形式:自动传播异常(
launch
与actor
)或向用户暴露异常(async
与produce
),前者这类构建器将异常视为未捕获异常,直接抛出,后者依赖用户来最终消费异常fun test_channel() = runBlocking { val job = GlobalScope.launch { // launch 根协程 println("Throwing exception from launch") throw IndexOutOfBoundsException() // 我们将在控制台打印 Thread.defaultUncaughtExceptionHandler } job.join() println("Joined failed job") val deferred = GlobalScope.async { // async 根协程 println("Throwing exception from async") throw ArithmeticException() // 没有打印任何东西,依赖用户去调用等待 } try { deferred.await() println("Unreached") } catch (e: ArithmeticException) { println("Caught ArithmeticException") } }
竞态与并发
-
协程可用多线程调度器并发执行,需要考虑同步访问共享的可变状态
-
方法一:对线程、协程都有效的常规解决方法,使用线程安全的数据类型
suspend fun massiveRun(action: suspend () -> Unit) { val n = 100 // 启动的协程数量 val k = 1000 // 每个协程重复执行同一动作的次数 val time = measureTimeMillis { coroutineScope { // 协程的作用域 repeat(n) { launch { repeat(k) { action() } } } } } println("Completed ${n * k} actions in $time ms") } var counter = AtomicInteger() @Test fun test_channel() = runBlocking { withContext(Dispatchers.Default) { massiveRun { counter.incrementAndGet() } } println("Counter = $counter") }
-
方法二: 对特定共享状态的所有访问权都限制在单个线程中
suspend fun massiveRun(action: suspend () -> Unit) { val n = 100 // 启动的协程数量 val k = 1000 // 每个协程重复执行同一动作的次数 val time = measureTimeMillis { coroutineScope { // 协程的作用域 repeat(n) { launch { repeat(k) { action() } } } } } println("Completed ${n * k} actions in $time ms") } val counterContext = newSingleThreadContext("CounterContext") var counter = 0 @Test fun test_channel() = runBlocking { withContext(Dispatchers.Default) { massiveRun { withContext(counterContext) { counter++ } } } println("Counter = $counter") }
-
方法三:将线程限制是在更大段代码中执行的
suspend fun massiveRun(action: suspend () -> Unit) { val n = 100 // 启动的协程数量 val k = 1000 // 每个协程重复执行同一动作的次数 val time = measureTimeMillis { coroutineScope { // 协程的作用域 repeat(n) { launch { repeat(k) { action() } } } } } println("Completed ${n * k} actions in $time ms") } val counterContext = newSingleThreadContext("CounterContext") var counter = 0 @Test fun test_channel() = runBlocking { withContext(counterContext) { massiveRun { counter++ } } println("Counter = $counter") }
-
方法四: 互斥锁
suspend fun massiveRun(action: suspend () -> Unit) { val n = 100 // 启动的协程数量 val k = 1000 // 每个协程重复执行同一动作的次数 val time = measureTimeMillis { coroutineScope { // 协程的作用域 repeat(n) { launch { repeat(k) { action() } } } } } println("Completed ${n * k} actions in $time ms") } val mutex = Mutex() var counter = 0 @Test fun test_channel() = runBlocking { withContext(Dispatchers.Default) { massiveRun { mutex.withLock { counter++ } } } println("Counter = $counter") }
-
方法五: 使用
Actors
结合通道,区分加数据和取数据,一个 actor 是一个协程suspend fun massiveRun(action: suspend () -> Unit) { val n = 100 // 启动的协程数量 val k = 1000 // 每个协程重复执行同一动作的次数 val time = measureTimeMillis { coroutineScope { // 协程的作用域 repeat(n) { launch { repeat(k) { action() } } } } } println("Completed ${n * k} actions in $time ms") } fun CoroutineScope.counterActor() = actor<CounterMsg> { var counter = 0 // actor 状态 for (msg in channel) { // 即将到来消息的迭代器 when (msg) { is IncCounter -> counter++ is GetCounter -> msg.response.complete(counter) } } }
最后
针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》
往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。
技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!**
CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》
往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。
[外链图片转存中…(img-y1B3iLUo-1630851049662)]