协程(又名纤程),轻量级线程(建立在线程基础上,属于用户态调用),非阻塞式编程(像同步编写一样),在用户态内进行任务调度,避免与内核态过多交互问题,提高程序快速响应。协程使用挂起当前上下文替代阻塞,被挂起后的协程可以去运行其它active task,即协程可以被复用,相比于线程,减少了线程资源的大量浪费。

备注

挂起:保存当前运行状态,释放资源,此时协程可去做其它工作,可充分利用资源
阻塞:占用资源未释放,等待状态

基本使用:

fun runAsync()= runBlocking {
val time = measureTimeMillis {//系统函数统计时间
val one = async { doSomethingUsefulOne() }//异步调用,返回结果
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")//等待异步执行完成(await调用会挂起当前线程,等待执行结果完成后,通过调用resume恢复挂起前状态)
}
println("Completed in $time ms")
} //协程coroutines 调用的方法需要用suspend修饰,告诉编译器此函数可以被挂起
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
} suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}

这里面没有使用异步+回调,直接像写同步代码一样,简洁

launch 异步执行没有返回结果,产生Job对象用于cancel,join处理

fun cancelCoroutine() = runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (isActive) { // cancellable computation loop
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}

线程之间切换,使用withContext

fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
fun jumpCor(){//创建单线程coroutines
newSingleThreadContext("Ctx1").use { ctx1 ->
newSingleThreadContext("Ctx2").use { ctx2 ->
runBlocking(ctx1) {
log("Started in ctx1")
withContext(ctx2) {
log("Working in ctx2")
}
log("Back to ctx1")
}
}
}
}

协程必须关联CoroutineScope以便于管理追踪,方法内创建Scope

suspend fun showSomeData() = coroutineScope {//此处coroutineScope属于out scope的child scop
val data = async(Dispatchers.IO) { // IO task io线程调用操作
// ... load some UI data for the Main thread ...
} withContext(Dispatchers.Main){//UI task UI更新
val result = data.await()
// display(result)
}
}

协程上下文环境,CoroutineScope,CoroutineContext

每个协程运行需要在指定Scope内才能使用协程相关方法delay,asyc,launch,创建CoroutineScope ,runBlocking函数内部会创建CoroutineScope,系统提供GlobalScope,MainScope等辅助类创建Scope

也可以通过CoroutineContext和Job创建自己的CoroutineScope

fun sampleCreateCorountine(){
//create corountine scope
//自定义CoroutineScope
val coroutineContext = Dispatchers.Default
val job = Job()
val coroutineScope = CoroutineScope(coroutineContext + job)
//创建child scope
coroutineScope.launch { }
//创建全局Scope
GlobalScope.launch (Dispatchers.Default+CoroutineName("global background thread")){ }
//创建主线程分发处理Scope
MainScope().launch { } }

类内部定义协程

1,直接继承CoroutineScope

class SomethingWithLifecycle : CoroutineScope {
// 使用job来管理你的SomethingWithLifecycle的所有子协程
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job fun destory(){//退出取消
job.cancel()
}
}

2,直接使用已定义Scope

class CorMyActivity : AppCompatActivity(), CoroutineScope by MainScope() {

    override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
showSomeData()
} /**
* Note how coroutine builders are scoped: if activity is destroyed or any of the launched coroutines
in this method throws an exception, then all nested coroutines are cancelled.
*/
fun showSomeData() = launch {
// <- extension on current activity, launched in the main thread
// ... here we can use suspending functions or coroutine builders with other dispatchers
// draw(data) // draw in the main thread
} override fun onDestroy() {
super.onDestroy()
cancel()
} }

Dispatchers,协程分发器:

fun dispatchTask()= runBlocking<Unit> {
// it inherits the context (and thus dispatcher) from the CoroutineScope that it is being launched from.
launch { // context of the parent, main runBlocking coroutine
println("main runBlocking : I'm working in thread ${Thread.currentThread().name}")
}
//执行coroutine是在调用者的线程,但是当在coroutine中第一个挂起之后,后面所在的线程将完全取决于
// 调用挂起方法的线程(如delay一般是由kotlinx.coroutines.DefaultExecutor中的线程调用)
//Unconfined在挂起后在delay的调用线程DefaultExecutor执行
launch(context = Dispatchers.Unconfined) { // not confined -- will work with main thread
println("Unconfined : I'm working in thread ${Thread.currentThread().name}")
}
// coroutines are launched in GlobalScope,uses shared background pool of threads
//uses the same dispatcher as GlobalScope.launch
  //Dispatchers.Default 处理cup密集型任务,线程数为cpu内核数,最少为2,Dispatchers.IO 处理阻塞性IO,socket密集度任务,数量随任务多少变化,默认最大数量64
launch(context = Dispatchers.Default) { // will get dispatched to DefaultDispatcher
println("Default : I'm working in thread ${Thread.currentThread().name}")
}
//creates a thread for the coroutine to run
launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread
println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
} }

suspend 是如何工作的? 
Kotlin 使用堆栈帧来管理要运行哪个函数以及所有局部变量。暂停协程时,
会复制并保存当前的堆栈帧以供稍后使用。恢复协程时,调度器会将堆栈帧从其保存位置复制回来,然后函数再次开始运行

协程间通信之channel

协程之间通过channel进行数据传递,生产者->消费者模式

例:

fun channelTest()= runBlocking {
val channel = Channel<Int>()
launch {//生产数据
for (x in 1..5) channel.send(x * x)
channel.close() //关闭停止
}
// 循环接收直到channnel close
for (y in channel) println(y)
println("Done!")
}

生产者每生产一个数据就发送到channel里,消费者等待接收数据,

channel分类:

SendChannel:创建的producers类型属于sendChannel实例

ReceiveChannel:创建的consumers类型属于receiveChannel实例

Channel:继承SendChannel和ReceiveChannel即可send,又可以receive数据

channel类型:

Unlimited channel:容量无限制,producer不断生产数据,可能会产生OutOfMemoryException,consumer接收数据时,如果channel内数据为空则会挂起
Buffered channel:指定 channel size,当生产者的数据达到buffer size大小则send会挂起,直到channel内数据量小于size才能继续生产数据
Rendezvous:是bufferred channel size=0,当producer生成数据send时如果没有consumer接受,则producer会挂起直到consumer取走数据,才继续send下一个数据,即实现同步传递数据功能
Conflated channel:producer不停地send数据,后面的数据会覆盖前面已经存在的数据,consumer始终取到最新的数据

    val rendezvousChannel = Channel<String>()//同步传递
val bufferedChannel = Channel<String>(10)//指定size pool
val conflatedChannel = Channel<String>(Channel.CONFLATED)//channel内数据实时更新
val unlimitedChannel = Channel<String>(Channel.UNLIMITED)//无容量限制

协程结合Architecture ViewModel使用

class NewsViewModel: ViewModel() {

    private val mApi:WebServer
init {
mApi = WebServer()
} val dataNews: MutableLiveData<DataResource<NewsDataRsp>> by lazy {
// MutableLiveData<DataResource<NewsDataRsp>>().also {
// loadNewsData(minId=null)
// }
MutableLiveData<DataResource<NewsDataRsp>>()
} fun loadNewsData(pageIndex:Int =1,countItem:Int = 20,minId:String?=null){
runCoroutine(dataNews){
val mp = mutableMapOf("encode" to "ywjh","source" to "app","sys" to "android","banner" to "banner",
"limit" to countItem.toString(),"version" to "7002000")
if(pageIndex>1 && false==minId.isNullOrEmpty()){
mp.put("min_id",minId)
}
val response = mApi.commonDataSourceApi.getNewsData(mp).execute()
return@runCoroutine response.body()!!
}
} fun fetchNews(pageIndex:Int =1,countItem:Int = 20,minId:String){
val mp = mutableMapOf("encode" to "ywjh","source" to "app","sys" to "android","banner" to "banner",
"limit" to countItem.toString(),"version" to "7002000")
if(pageIndex>1 && false==minId.isNullOrEmpty()){
mp.put("min_id",minId)
} val cor = CoroutineScope(Dispatchers.IO)
cor.launch {
try {
val response = mApi.commonDataSourceApi.getNewsData(mp).execute()
dataNews.postValue(DataResource(DataResource.Status.COMPLETED, response.body(), null))
} catch (exception: Exception) {
dataNews.postValue(DataResource(DataResource.Status.COMPLETED, null, exception))
}
}
} suspend fun simpleGetData(pageIndex:Int =1,countItem:Int = 20,minId:String) = withContext(Dispatchers.IO) {
val mp = mutableMapOf("encode" to "ywjh","source" to "app","sys" to "android","banner" to "banner",
"limit" to countItem.toString(),"version" to "7002000")
if(pageIndex>1 && false==minId.isNullOrEmpty()){
mp.put("min_id",minId)
} try {
val response = mApi.commonDataSourceApi.getNewsData(mp).execute()
dataNews.postValue(DataResource(DataResource.Status.COMPLETED, response.body(), null))
} catch (exception: Exception) {
dataNews.postValue(DataResource(DataResource.Status.COMPLETED, null, exception))
}
} private fun <T> runCoroutine(correspondenceLiveData: MutableLiveData<DataResource<T>>, block: suspend () -> T) {
correspondenceLiveData.value = DataResource(DataResource.Status.LOADING, null, null) GlobalScope.launch(Dispatchers.IO) {
try {
val result = block()
correspondenceLiveData.postValue(DataResource(DataResource.Status.COMPLETED, result, null))
} catch (exception: Exception) {
// val error = ErrorConverter.convertError(exception)
correspondenceLiveData.postValue(DataResource(DataResource.Status.COMPLETED, null, exception))
}
}
} }

kotlin学习-Coroutines(协程)的更多相关文章

  1. swoole深入学习 8. 协程 转

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/yangyi2083334/article/ ...

  2. Python学习---线程/协程/进程学习 1220【all】

    Python学习---线程基础学习 Python学习---线程锁/信号量/条件变量同步1221 Python学习---同步条件event/队列queue1223 Python学习---进程 1225 ...

  3. python学习笔记 协程

    在学习异步IO模型前,先来了解协程 协程又叫做微线程,Coroutine 子程序或者成为函数,在所有语言中都是层级调用,比如a调用b,b调用c.c执行完毕返回,b执行完毕返回,最后a执行完毕返回 所以 ...

  4. python学习之-- 协程

    协程(coroutine)也叫:微线程,是一种用户态的轻量级线程,就是在单线程下实现并发的效果.优点:1:无需线程上下文切换的开销.(就是函数之间来回切换)2:无需原子操作锁定及同步的开销.(如改一个 ...

  5. Python学习之协程

    8.8 协程 ​ 我们都知道线程间的任务切换是由操作系统来控制的,而协程的出现,就是为了减少操作系统的开销,由协程来自己控制任务的切换 ​ 协程本质上就是线程.既然能够切换任务,所以线程有两个最基本的 ...

  6. Python学习笔记--协程asyncio

    协程的主要功能是单线程并发运行 假设有3个耗时不一样的任务.看看协程的效果. 先来看没有使用协程情况: #!/usr/bin/python3 # -*- coding:utf-8 -*- import ...

  7. Kotlin协程第一个示例剖析及Kotlin线程使用技巧

    Kotlin协程第一个示例剖析: 上一次https://www.cnblogs.com/webor2006/p/11712521.html已经对Kotlin中的协程有了理论化的了解了,这次则用代码来直 ...

  8. Kotlin Coroutine(协程): 一、样例

    @ 目录 前言 一.直接上例子 1.延时任务. 2.异步任务 3.并行任务: 4.定时任务: 总结 前言 你还在用 Hanlder + Message? 或者 AsyncTask? 你还在用 Rxja ...

  9. Kotlin协程入门

    开发环境 IntelliJ IDEA 2021.2.2 (Community Edition) Kotlin: 212-1.5.10-release-IJ5284.40 介绍Kotlin中的协程.用一 ...

  10. Android中的Coroutine协程原理详解

    前言 协程是一个并发方案.也是一种思想. 传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高.但是面对计算密集型的任务不如多线程并行运算效率高. 不同的语言对于协程都有不同的实 ...

随机推荐

  1. Python3.7.3环境搭建

    Python3.7.3安装(Win10) 到2019年初,Python3已经更新到了Python3.7.3,Python有两个大版本Python2和Python3,Python3是现在和未来的主流. ...

  2. python解释器下载与基本使用

    python介绍与解释器下载基本使用 1.python发展方向 ​ web方向.自动化运维.自动化测试.自动化办公.网络爬虫.金融量化.人工智能.机器学习.数据分析 2.python解释器 ​ 历史 ...

  3. 为什么NoSQL数据库这么受欢迎?

    大数据时代,NoSQL数据库是企业构建数据能力的核心工具之一.近期,在2022腾讯全球数字生态大会NoSQL数据库专场上,腾讯云发布了多项NoSQL产品能力升级,并重点讲解了其背后的自研技术要点及实现 ...

  4. c语言学习总结(原创)

    什么是标识符? 标识符是用来标识变量.函数.类.模块,或者任何其他用户自定义项目的名称,用它来命名程序正文中的一些实体,比如函数名.变量名.类名.对象名等.如:int a1=0; const b1=& ...

  5. 企业应用架构研究系列十三:整合EFCore&Dapper 通用ORM框架EFDapper

    EntityFrameworkCore是微软官网提供的ORM框架,是轻量化.可扩展.开源和跨平台的数据访问技术框架,但是在.Net 开发圈的评论却褒贬不一.很多人认为EFCore 执行的效能比较差,很 ...

  6. 複合語句塊——關於while循環的

    看這兩個小段代碼: /*code1.*/ index=0; while(index<10) sam = 10*index+2; printf("sam=%d\n",sam); ...

  7. SQLSERVER 的四个事务隔离级别到底怎么理解?

    一:背景 1. 讲故事 在有关SQLSERVER的各种参考资料中,经常会看到如下四种事务隔离级别. READ UNCOMMITTED READ COMMITTED SERIALIZABLE REPEA ...

  8. By not providing "FindQt5.cmake" in CMAKE_MODULE_PATH this project has asked CMake to find a package configuration file provided by "Qt5", but CMake did not find one.

    环境 qt5.12.3  deepin15.10 cmake构建 由于之前使用的是仓库自带的qt环境,后来需要更高版本qt,于是从官网下载安装器自己安装,重新构建之后便出现这个问题,具体报错如下 CM ...

  9. .NET与大数据

    前言 当别人做大数据用Java.Python的时候,我使用.NET做大数据.数据挖掘,这确实是值得一说的事. 写的并不全面,但都是实际工作中的内容. .NET在大数据项目中,可以做什么? 写脚本(使用 ...

  10. 【随笔记】全志平台 gpio-leds 驱动应用

    硬件信息 内核版本:Linux 4.9 硬件原理: GPIO 通过一颗 MOS 管(2N7002ET1G),接到 LED 负极,LED 正极接电源. GPIO 通过拉高导通 MOS ,将 LED 接地 ...