草庐IT

kotlin协程小记

妖久 2023-03-28 原文

例子一:

GlobalScope.launch(Dispatchers.Main) {
    //开启子协程
    withContext(Dispatchers.IO) {
        for (i in 0 until 1000) {
        }
        Log.d("MainActivityXX", "withContext-> thread:" + Thread.currentThread().name)
    }
    Log.d("MainActivityXX", "GlobalScope-> thread:" + Thread.currentThread().name)
}
Log.d("MainActivityXX", "onCreate-> thread:" + Thread.currentThread().name)

打印log:

2022-10-09 20:24:21.100 8371-8371/com.example.xiecheng D/MainActivityXX: onCreate-> thread:main
2022-10-09 20:24:21.131 8371-8412/com.example.xiecheng D/MainActivityXX: withContext-> thread:DefaultDispatcher-worker-1
2022-10-09 20:24:21.258 8371-8371/com.example.xiecheng D/MainActivityXX: GlobalScope-> thread:main

 

例子二:

GlobalScope.launch(Dispatchers.Main) {
    //开启子协程
    withContext(Dispatchers.IO) {
        for (i in 0 until 1000) {
        }
        Log.d("MainActivityXX", "withContext1-> thread:" + Thread.currentThread().name)
        withContext(Dispatchers.IO) {
            for (i in 0 until 1000) {
            }
            Log.d("MainActivityXX", "withContext2-> thread:" + Thread.currentThread().name)
        }
    }
    withContext(Dispatchers.IO) {
        for (i in 0 until 1000) {
        }
        Log.d("MainActivityXX", "withContext3-> thread:" + Thread.currentThread().name)
    }
    Log.d("MainActivityXX", "GlobalScope-> thread:" + Thread.currentThread().name)
}
Log.d("MainActivityXX", "onCreate-> thread:" + Thread.currentThread().name)

打印log:

onCreate-> thread:main
withContext1-> thread:DefaultDispatcher-worker-1
withContext2-> thread:DefaultDispatcher-worker-1
withContext3-> thread:DefaultDispatcher-worker-1
GlobalScope-> thread:main

主线程开启一个协程,并不会阻碍主线程的执行,单个协程内部是串行执行的,这里整一个都是串行执行的是因为withContext是一个挂起函数。

 

GlobalScope.launch(Dispatchers.Main) {
    //开启子协程
    GlobalScope.launch (Dispatchers.IO) {
        Log.d("MainActivityXX", "withContext-> thread:" + Thread.currentThread().name)
    }
    Log.d("MainActivityXX", "GlobalScope-> thread:" + Thread.currentThread().name)
}

打印log:

GlobalScope-> thread:main
withContext-> thread:DefaultDispatcher-worker-1

 

例子二:

GlobalScope.launch(Dispatchers.Main) {
    //开启子协程
    withContext(Dispatchers.IO) {
        get()
        Log.d("MainActivityXX", "withContext-> thread:" + Thread.currentThread().name)
    }
    Log.d("MainActivityXX", "GlobalScope-> thread:" + Thread.currentThread().name)
}

打印log:

2022-11-01 20:56:25.220 20058-20100/com.example.xiecheng D/MainActivityXX: withContext-> thread:DefaultDispatcher-worker-1
2022-11-01 20:56:25.264 20058-20058/com.example.xiecheng D/MainActivityXX: GlobalScope-> thread:main
2022-11-01 20:56:30.222 20058-20103/com.example.xiecheng D/MainActivityXX: get-> thread:Thread-8

协程里面开启线程,那么线程不会受协程影响。

 

例子三:

GlobalScope.launch(Dispatchers.Main) {
    fetchDocs()
}

suspend fun fetchDocs() {                             // Dispatchers.Main
    val result = get("https://developer.android.com") // Dispatchers.IO for `get`
    Log.d("MainActivityXX", "fetchDocs-> thread:" + Thread.currentThread().name) 
}
//withContext本身就是挂起函数
suspend fun get(url: String) = withContext (Dispatchers.IO) {
    Log.d("MainActivityXX", "get-> thread:" + Thread.currentThread().name)
}

打印log:

get-> thread:DefaultDispatcher-worker-1
fetchDocs-> thread:main

使用了挂起函数,会先等待get方法执行完再执行下面得操作。

 

1、挂起函数并不会阻塞其所在线程,这样就极大地提高了线程的并发灵活度,最大化了线程的利用效率。
当在 ThreadA 上运行的 CoroutineA 调用了delay(1000L)函数指定延迟一秒后再运行,ThreadA 会转而去执行 CoroutineB,等到一秒后再来继续执行 CoroutineA

2、withContext本身就是挂起函数。

3、挂起的对象是协程。

4、这个 suspend 关键字,既然它并不是真正实现挂起,它其实是一个提醒。函数的创建者对函数的使用者的提醒:我是一个耗时函数,我被我的创建者用挂起的方式放在后台运行,所以请在协程里调用我。
为什么 suspend 关键字并没有实际去操作挂起,但 Kotlin 却把它提供出来?
因为它本来就不是用来操作挂起的。挂起的操作 —— 也就是切线程,依赖的是挂起函数里面的实际代码,而不是这个关键字。
所以这个关键字,只是一个提醒。
所以,创建一个 suspend 函数,为了让它包含真正挂起的逻辑,要在它内部直接或间接调用 Kotlin 自带的 suspend 函数,你的这个 suspend 才是有意义的。


5、所以这个 suspend,其实并不是起到把任何把协程挂起,或者说切换线程的作用。真正挂起协程这件事,是 Kotlin 的协程框架帮我们做的。
所以我们想要自己写一个挂起函数,仅仅只加上 suspend 关键字是不行的,还需要函数内部直接或间接地调用到 Kotlin 协程框架自带的 suspend 函数才行。

6、开发者需要明白,协程是运行于线程上的,一个线程可以运行多个(可以是几千上万个)协程。线程的调度行为是由 OS 来操纵的,而协程的调度行为是可以由开发者来指定并由编译器来实现的。当协程 A 调用 delay(1000L) 函数来指定延迟1秒后再运行时,协程 A 所在的线程只是会转而去执行协程 B,等到1秒后再把协程 A 加入到可调度队列里。所以说,线程并不会因为协程的延时而阻塞,这样可以极大地提高线程的并发灵活度。

 

有关kotlin协程小记的更多相关文章

  1. [面试直通版]操作系统核心之进程、线程与协程(下) - 2

    点击->操作系统复习的文章集目录操作系统线程线程是什么进程与线程的关系用户态/内核态操作系统资源管理内核态用户态内核态/用户态切换程序运行类型分析计算密集型IO密集型结合进程,线程来理解程序运行类型分析协程基础上下文切换协程协程为什么叫协作式线程?协程的优缺点操作系统线程典型问题:简述进程和线程的区别以下内容带您一步步了解线程是什么比进程更小的独立运行的基本单位-线程(Threads)线程的提出主要是为了提高系统内程序并发执行的程度,从而进一步提升系统的吞吐量,充分发挥多核CPU的优越性而设计的引入进程是为了操作系统更加方便地管理程序,使得多个程序能并发管理和执行而线程则是为了减少程序在并发执

  2. Kotlin:通过并消耗2个参数函数? - 2

    我正在尝试在Kotlin学习功能编程,并且难以使此代码起作用:importjava.util.*funcaseName(br:String,c:Int):String{if(c==0){returnbr.toLowerCase()}else{returnbr.toUpperCase()}}funmapIt(ns:ArrayList,f:(String,Int)->String):List{valcoll:List=ns.map{it->f(it,_)}returncoll}funmain(args:Array){valnames=arrayListOf("Joe","Bill","Murrar

  3. performance - 协程性能 - 2

    我已经开始学习围棋了,它既有趣又简单。但是使用goroutines我没有看到性能上的好处。如果我尝试在2个函数中两次连续添加100万个数字:packagemainimport("fmt""time")varsumAintvarsumBintfuncfSumA(){fori:=0;i需要5毫秒。MacBook-Pro-de-Pedro:hellopedro$./bin/helloElapsedtime5.724406msSumatotal999999000000MacBook-Pro-de-Pedro:hellopedro$./bin/helloElapsedtime5.358165ms

  4. go - 如何等待 panic 的协程? - 2

    等待goroutine的常见方法是使用*sync.WaitGroup:funcmain(){wg:=&sync.WaitGroup{}wg.Add(1)gofunc(){deferwg.Done()//Longrunningtask}()wg.Wait()}这里没有问题。但是,这个怎么样:funcmain(){wg:=&sync.WaitGroup{}wg.Add(1)gofunc(){deferwg.Done()//Longrunningtaskpanic("Somethingunexpectedhappened.")}()wg.Wait()}在这种情况下,当wg.Done()被调用

  5. go - 如何正确限制协程的数量 - 2

    我正在进入URL的“stdin”行,例如:$echo-e'https://golang.org\nhttps://godoc.org\nhttps://golang.org'|去运行1.go。任务是从每个网页中获取单词“Go”的编号。但是我不允许启动超过5个goroutines并且只能使用标准库这是我的代码:packagemainimport("fmt""net/http""bufio""os""regexp""io/ioutil""time")funcworker(idint,jobs在我将超过5个URL(其中一个不正确)传递给标准输入之前,我一直认为它有效。输出是:goroutin

  6. Golang 中的协程大小不是线性增加的 - 2

    对于下面的代码:constLOOPNUMint=200000funcmain(){z:=make(chanint16)fori:=0;i我用LOOPNUM=200k和400k运行代码,内存使用情况如下:有谁知道我将goroutines加倍后内存突然增加的原因(以及减少内存使用的任何解决方案)?谢谢! 最佳答案 您不是在等待goroutines完成,因此它会在更改以执行您告诉它的所有操作之前退出。将其更改为:constLOOPNUMint=200000varwgsync.WaitGroupfuncmain(){wg=sync.Wait

  7. 带有 http.HandleFunc 的协程 - 2

    我想知道代码1是否管理内部goutines并且可以在请求增加(几十个)时使用一个CPU的所有内核,或者如果每个处理程序我必须放置关键字go来指示函数处理程序将由一个gorotine管理,如代码2中所示,因此可以使用服务器的所有核心。代码1packagemainimport("fmt""net/http")funcHandlerOne(whttp.ResponseWriter,req*http.Request){fmt.Println("messageone")}funcHandlerTwo(whttp.ResponseWriter,req*http.Request){fmt.Print

  8. go - CoInitializeEx(COINIT_MULTITHREADED) 和使用 WMI 的协程 - 2

    我们有一个用Go编写的监控代理,它使用许多goroutines从WMI收集系统指标。我们最近发现当go二进制文件在Server2016或Windows10(也可能在其他使用WMF5.1的操作系统上)上运行时,程序会泄漏内存。在为reproducetheissue创建最小测试用例之后似乎仅当您对ole.CoInitializeEx方法进行大量调用时才会发生泄漏(WMF5.1中可能发生了一些变化,但我们无法在同一系统上使用pythoncomtypes包重现该问题)。我们在应用程序中将COINIT_MULTITHREADED用于多线程单元(MTA),我的问题是:因为我们从各种goroutin

  9. Golang 同步协程 - 2

    我正在开发用于并发配置网络设备的SSH客户端应用程序,但在实现并发性时遇到了问题。我的程序接受一部分主机和一部分配置命令以发送到每个主机。我正在使用sync.WaitGroup来等待所有主机完成配置。这适用于小批量主机,但很快我的配置goroutines中的功能开始随机失败。如果我在失败的主机上重新运行程序,一些会成功,一些会再次失败。我必须重复这个过程,直到只剩下有实际错误的主机。它总是在身份验证失败时说authenticationfailed:authmethodstried[nonepassword]...或者sysDescr的值没有添加到某些设备字段。就好像当有许多主机和gor

  10. 协程死锁? - 2

    我是golang的新手,我对这个死锁感到困惑(runhere)packagemainimport("fmt""runtime""time")funcmain(){c:=make(chanstring)work:=make(chanint,1)clvl:=runtime.NumCPU()count:=0fori:=0;i 最佳答案 您永远不会关闭c,因此您的forrange循环将永远等待。像这样关闭它:varwgsync.WaitGroupfori:=0;i编辑:修复了panic,感谢Crast

随机推荐