協程(Coroutine-輕量級線程)的使用

一般我們需要異步操作的時候,都是通過多線程來實現的,但是線程的創建和開啓是個耗性能的操作,開少量的線程沒有問題,可是線程多了就有問題了,而且線程還會有各種數據同步的問題。
協程作爲一個輕量級的線程,可以完美解決這些問題。你試下開100萬個線程?不死機我吃電腦。但是你開100萬個協程,那是分分鐘的事。
這裏只說androidkotlin中的使用。

添加依賴

// kotlin 版本
ext.kotlin_version = '1.3.41'

// repository 
jcenter()

// 依賴
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-RC2'

GlobalScope.launch

fun main() {
    GlobalScope.launch {
        println("我是一個協程")
        delay(1000)
        println("我不會打印,因爲主線程已經退出了")
    }
    // 和上面的代碼並行執行
    Thread.sleep(500)
    println("主線程退出了")
}

輸出:
我是一個協程
主線程退出了

runBlocking

fun main() {
    runBlocking {
        launch {
            println("協程開始")
            delay(1000)
            println("協程結束")
        }
    }
    // 會阻塞等待 runBlocking 中的代碼執行完畢
    Thread.sleep(500)
    println("主線程退出了")
}

輸出:
協程開始
協程結束
主線程退出了

suspend

fun main() {
    runBlocking {
        println("開始")
        launch {
            test()
        }
        println("結束")
    }
}

suspend fun test() {
    delay(1000L)
    println("我是suspend方法打印的")
}

輸出:
開始
結束
我是suspend方法打印的

Job

fun main() {
    runBlocking {
        println("開始")
        val job = launch {
            delay(1000L)
            println("in launch")
        }
        job.join()
        println("結束")
    }
}

輸出:
開始
in launch
結束

cancelAndJoin

Job.cancel 實際上是拋出異常,會直接中斷執行。

fun main() {
    runBlocking {
        println("開始")
        val job = launch {
            try {
                var i = 1
                while (isActive) {
                    print(i++)
                    delay(500L)
                }
            } finally {
                println("\n異常退出")
            }
        }
        delay(1300L)
        job.cancelAndJoin()// cancel實質上是拋出異常,所以會直接停止,而不會等到delay完後再停止
        println("結束")
    }
}

輸出:
開始
123
異常退出
結束

withTimeout

Timeout實際上也是拋出異常。

fun main() {
    runBlocking {
        println("開始")
        try {
            val result = withTimeout(1300) {
                repeat(10) {
                    print(it)
                    delay(500)
                }
                "正常完成,沒有Timeout"
            }
            println("\n結束 result=$result")
        } catch (e: TimeoutCancellationException) {
            println("\n結束 Timeout了")
        }
    }
}

輸出:
開始
012
結束 Timeout了

Channel

Channel相當於BlockingQueue

fun main() {
    runBlocking {
        println("開始")
        val channel = Channel<Int>()
        launch {
            for (x in 1..5) channel.send(x * x)
            channel.close()
        }
        for (y in channel) print("$y ")
        println("\n結束")
    }
}

輸出:
開始
1 4 9 16 25
結束

async、await

fun main() {
    runBlocking {
        println("開始")
        val d = async {
            println("async start...")
            delay(1000)
            println("async end")
            999
        }
        println("d=${d.await()}")
        println("結束")
    }
}

如果想要 async 不自行執行,加上 CoroutineStart.LAZY 即可,然後 async 將會等待調用 start 或者 await 後纔會執行,如下:

fun main() {
    runBlocking {
        println("開始")
        val d = async(start = CoroutineStart.LAZY) {
            println("async start...")
            delay(1000)
            println("async end")
            999
        }
        d.start()
        println("d=${d.await()}")
        println("結束")
    }
}

輸出:
開始
async start…
async end
d=999
結束

真正執行線程

fun main() {
    runBlocking {
        // 默認執行在當前線程
        launch {
            println("launch\t\t\t\t\t" + Thread.currentThread().name)
        }

        // 默認執行在工作線程
        GlobalScope.launch {
            println("GlobalScope.launch\t\t" + Thread.currentThread().name)
        }

        // 默認執行在工作線程
        launch(Dispatchers.Default) {
            println("Dispatchers.Default\t\t" + Thread.currentThread().name)
        }

        // 默認執行在工作線程
        launch(Dispatchers.IO) {
            println("Dispatchers.IO\t\t\t" + Thread.currentThread().name)
        }

        // 不限制的,可能執行在任何線程,甚至裏面的不同行代碼也會執行在不同的線程
        launch(Dispatchers.Unconfined) {
            println("Dispatchers.Unconfined\t" + Thread.currentThread().name)
        }

        // 只有android中才能用main
//        launch(Dispatchers.Main) {
//            println("Dispatchers.Main\t\t" + Thread.currentThread().name)
//        }
    }
}

輸出:
GlobalScope.launch DefaultDispatcher-worker-1
Dispatchers.Default DefaultDispatcher-worker-3
Dispatchers.IO DefaultDispatcher-worker-3
Dispatchers.Unconfined main
launch main

官方文檔

https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html
https://github.com/Kotlin/kotlinx.coroutines

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章