欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

kotlin協(xié)程——>協(xié)程上下文與調(diào)度器-創(chuàng)新互聯(lián)

協(xié)程上下?與調(diào)度器kotlin協(xié)程——>協(xié)程
上下文與調(diào)度器

協(xié)程總是運?在?些以 CoroutineContext 類型為代表的上下?中,它們被定義在了 Kotlin 的標(biāo)準(zhǔn)庫 ?。 協(xié)程上下?是各種不同元素的集合。其中主元素是協(xié)程中的 Job,我們在前?的?檔中?過它以及它的 調(diào)度器,?本?將對它進(jìn)?介紹。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:空間域名、虛擬主機(jī)、營銷軟件、網(wǎng)站建設(shè)、奈曼網(wǎng)站維護(hù)、網(wǎng)站推廣。

調(diào)度器與線程

協(xié)程上下?包含?個 協(xié)程調(diào)度器(參? CoroutineDispatcher)它確定了哪些線程或與線程相對應(yīng)的 協(xié)程執(zhí)?。協(xié)程調(diào)度器可以將協(xié)程限制在?個特定的線程執(zhí)?,或?qū)⑺峙傻?個線程池,亦或是讓它 不受限地運?。 所有的協(xié)程構(gòu)建器諸如 launch 和 async 接收?個可選的 CoroutineContext 參數(shù),它可以被?來顯式 的為?個新協(xié)程或其它上下?元素指定?個調(diào)度器。

嘗試下?的?例:

launch { // 運?在?協(xié)程的上下?中,即 runBlocking 主協(xié)程 println("main runBlocking : I'm working in thread ${Thread.currentThread().name}") } launch(Dispatchers.Unconfined) { // 不受限的——將?作在主線程中 println("Unconfined : I'm working in thread ${Thread.currentThread().name}") } launch(Dispatchers.Default) { // 將會獲取默認(rèn)調(diào)度器 println("Default : I'm working in thread ${Thread.currentThread().name}") } launch(newSingleThreadContext("MyOwnThread")) { // 將使它獲得?個新的線程 println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}") }

它執(zhí)?后得到了如下輸出(也許順序會有所不同):

Unconfined : I'm working in thread main Default : I'm working in thread DefaultDispatcher-worker-1 newSingleThreadContext: I'm working in thread MyOwnThread main runBlocking : I'm working in thread main

當(dāng)調(diào)? launch { …… } 時不傳參數(shù),它從啟動了它的 CoroutineScope 中承襲了上下?(以及調(diào)度 器)。在這個案例中,它從 main 線程中的 runBlocking 主協(xié)程承襲了上下?。 Dispatchers.Unconfined 是?個特殊的調(diào)度器且似乎也運?在 main 線程中,但實際上,它是?種不 同的機(jī)制,這會在后?中講到。 當(dāng)協(xié)程在 GlobalScope 中啟動時,使?的是由 Dispatchers.Default 代表的默認(rèn)調(diào)度器。默認(rèn)調(diào)度器使 ?共享的后臺線程池。所以 launch(Dispatchers.Default) { …… } 與 GlobalScope.launch { …… } 使?相同的調(diào)度器。 newSingleThreadContext 為協(xié)程的運?啟動了?個線程。?個專?的線程是?種?常昂貴的資源。 在真實的應(yīng)?程序中兩者都必須被釋放,當(dāng)不再需要的時候,使? close 函數(shù),或存儲在?個頂層變量 中使它在整個應(yīng)?程序中被重?。

?受限調(diào)度器 vs 受限調(diào)度器

Dispatchers.Unconfined 協(xié)程調(diào)度器在調(diào)?它的線程啟動了?個協(xié)程,但它僅僅只是運?到第?個掛 起點。掛起后,它恢復(fù)線程中的協(xié)程,?這完全由被調(diào)?的掛起函數(shù)來決定。?受限的調(diào)度器?常適?于 執(zhí)?不消耗 CPU 時間的任務(wù),以及不更新局限于特定線程的任何共享數(shù)據(jù)(如UI)的協(xié)程。 另???,該調(diào)度器默認(rèn)繼承了外部的 CoroutineScope。runBlocking 協(xié)程的默認(rèn)調(diào)度器,特別是,當(dāng) 它被限制在了調(diào)?者線程時,繼承?它將會有效地限制協(xié)程在該線程運?并且具有可預(yù)測的 FIFO 調(diào) 度。

launch(Dispatchers.Unconfined) { // ?受限的——將和主線程?起?作 println("Unconfined : I'm working in thread ${Thread.currentThread().name}") delay(500) println("Unconfined : After delay in thread ${Thread.currentThread().name}") } launch { // ?協(xié)程的上下?,主 runBlocking 協(xié)程 println("main runBlocking: I'm working in thread ${Thread.currentThread().name}") delay(1000) println("main runBlocking: After delay in thread ${Thread.currentThread().name}") }

執(zhí)?后的輸出:

Unconfined : I'm working in thread main main runBlocking: I'm working in thread main Unconfined : After delay in thread kotlinx.coroutines.DefaultExecutor main runBlocking: After delay in thread main

所以,該協(xié)程的上下?繼承? runBlocking {...} 協(xié)程并在 main 線程中運?,當(dāng) delay 函數(shù)調(diào) ?的時候,?受限的那個協(xié)程在默認(rèn)的執(zhí)?者線程中恢復(fù)執(zhí)?。

?受限的調(diào)度器是?種?級機(jī)制,可以在某些極端情況下提供幫助?不需要調(diào)度協(xié)程以便稍后執(zhí)?或產(chǎn)?不希望的副作?,因為某些操作必須?即在協(xié)程中執(zhí)?。?受限調(diào)度器不應(yīng)該在通常的
代碼中使?。

調(diào)試協(xié)程與線程

協(xié)程可以在?個線程上掛起并在其它線程上恢復(fù)。甚??個單線程的調(diào)度器也是難以弄清楚協(xié)程在何 時何地正在做什么事情。使?通常調(diào)試應(yīng)?程序的?法是讓線程在每?個?志?件的?志聲明中打印 線程的名字。這種特性在?志框架中是普遍受?持的。但是在使?協(xié)程時,單獨的線程名稱不會給出很 多協(xié)程上下?信息,所以 kotlinx.coroutines 包含了調(diào)試?具來讓它更簡單。 使? -Dkotlinx.coroutines.debug JVM 參數(shù)運?下?的代碼:

val a = async { log("I'm computing a piece of the answer") 6 } val b = async { log("I'm computing another piece of the answer") 7 } log("The answer is ${a.await() * b.await()}")

這?有三個協(xié)程,包括 runBlocking 內(nèi)的主協(xié)程 (#1),以及計算延期的值的另外兩個協(xié)程 a (#2) 和 b (#3)。它們都在 runBlocking 上下?中執(zhí)?并且被限制在了主線程內(nèi)。這段代碼的輸出如下:

[main @coroutine#2] I'm computing a piece of the answer [main @coroutine#3] I'm computing another piece of the answer [main @coroutine#1] The answer is 42

這個 log 函數(shù)在?括號種打印了線程的名字,并且你可以看到它是 main 線程,并且附帶了當(dāng)前正在 其上執(zhí)?的協(xié)程的標(biāo)識符。這個標(biāo)識符在調(diào)試模式開啟時,將連續(xù)分配給所有創(chuàng)建的協(xié)程。

當(dāng) JVM 以 -ea 參數(shù)配置運?時,調(diào)試模式也會開啟。你可以在 DEBUG_PROPERTY_NAME 屬性 的?檔中閱讀有關(guān)調(diào)試?具的更多信息。

在不同線程間跳轉(zhuǎn)

使? -Dkotlinx.coroutines.debug JVM 參數(shù)運?下?的代碼

newSingleThreadContext("Ctx1").use { ctx1 -> newSingleThreadContext("Ctx2").use { ctx2 -> runBlocking(ctx1) { log("Started in ctx1") withContext(ctx2) { log("Working in ctx2") } log("Back to ctx1") } } }

它演?了?些新技術(shù)。其中?個使? runBlocking 來顯式指定了?個上下?,并且另?個使? withContext 函數(shù)來改變協(xié)程的上下?,?仍然駐留在相同的協(xié)程中,正如可以在下?的輸出中所?到的:

[Ctx1 @coroutine#1] Started in ctx1 [Ctx2 @coroutine#1] Working in ctx2 [Ctx1 @coroutine#1] Back to ctx1

注意,在這個例?中,當(dāng)我們不再需要某個在 newSingleThreadContext 中創(chuàng)建的線程的時候,它使? 了 Kotlin 標(biāo)準(zhǔn)庫中的 use 函數(shù)來釋放該線程。

上下?中的作業(yè)

協(xié)程的 Job 是上下?的?部分,并且可以使? coroutineContext [Job] 表達(dá)式在上下?中檢索 它:

println("My job is ${coroutineContext[Job]}")

在調(diào)試模式下,它將輸出如下這些信息:

My job is "coroutine#1":BlockingCoroutine{Active}@6d311334

請注意,CoroutineScope 中的 isActive 只是 coroutineContext[Job]?.isActive == true 的?種?便的快捷?式。

?協(xié)程

當(dāng)?個協(xié)程被其它協(xié)程在 CoroutineScope 中啟動的時候,它將通過 CoroutineScope.coroutineContext 來承襲上下?,并且這個新協(xié)程的 Job 將會成為?協(xié)程作業(yè)的?作業(yè)。當(dāng)?個?協(xié)程被取消的時候,所有它的?協(xié)程也會被遞歸的取消。 然?,當(dāng)使? GlobalScope 來啟動?個協(xié)程時,則新協(xié)程的作業(yè)沒有?作業(yè)。因此它與這個啟動的作?域?關(guān)且獨?運作。

// 啟動?個協(xié)程來處理某種傳?請求(request) val request = launch { // 孵化了兩個?作業(yè), 其中?個通過 GlobalScope 啟動 GlobalScope.launch { println("job1: I run in GlobalScope and execute independently!") delay(1000) println("job1: I am not affected by cancellation of the request") } // 另?個則承襲了?協(xié)程的上下? launch { delay(100) println("job2: I am a child of the request coroutine") delay(1000) println("job2: I will not execute this line if my parent request is cancelled") } } delay(500) request.cancel() // 取消請求(request)的執(zhí)? delay(1000) // 延遲?秒鐘來看看發(fā)?了什么 println("main: Who has survived request cancellation?")

這段代碼的輸出如下:

job1: I run in GlobalScope and execute independently! job2: I am a child of the request coroutine job1: I am not affected by cancellation of the request main: Who has survived request cancellation?

父協(xié)程的職責(zé)

?個?協(xié)程總是等待所有的?協(xié)程執(zhí)?結(jié)束。?協(xié)程并不顯式的跟蹤所有?協(xié)程的啟動,并且不必使? Job.join 在最后的時候等待它們:

// 啟動?個協(xié)程來處理某種傳?請求(request) val request = launch { repeat(3) { i -> // 啟動少量的?作業(yè) launch { delay((i + 1) * 200L) // 延遲 200 毫秒、400 毫秒、600 毫秒的時間 println("Coroutine $i is done") } } println("request: I'm done and I don't explicitly join my children that are still active") } request.join() // 等待請求的完成,包括其所有?協(xié)程 println("Now processing of the request is complete")

結(jié)果如下所?:

request: I'm done and I don't explicitly join my children that are still active Coroutine 0 is done Coroutine 1 is done Coroutine 2 is done Now processing of the request is complete

命名協(xié)程以用于調(diào)試

當(dāng)協(xié)程經(jīng)常打印?志并且你只需要關(guān)聯(lián)來?同?個協(xié)程的?志記錄時,則?動分配的 id 是?常好的。 然?,當(dāng)?個協(xié)程與特定請求的處理相關(guān)聯(lián)時或做?些特定的后臺任務(wù),最好將其明確命名以?于調(diào)試 ?的。CoroutineName 上下?元素與線程名具有相同的?的。當(dāng)調(diào)試模式開啟時,它被包含在正在執(zhí) ?此協(xié)程的線程名中。

log("Started main coroutine") // 運?兩個后臺值計算 val v1 = async(CoroutineName("v1coroutine")) { delay(500) log("Computing v1") 252 } val v2 = async(CoroutineName("v2coroutine")) { delay(1000) log("Computing v2") 6 } log("The answer for v1 / v2 = ${v1.await() / v2.await()}")

程序執(zhí)?使?了 -Dkotlinx.coroutines.debug JVM 參數(shù),輸出如下所?:

[main @main#1] Started main coroutine [main @v1coroutine#2] Computing v1 [main @v2coroutine#3] Computing v2 [main @main#1] The answer for v1 / v2 = 42

結(jié)合上下文中的元素

有時我們需要在協(xié)程上下?中定義多個元素。我們可以使? + 操作符來實現(xiàn)。?如說,我們可以顯式指 定?個調(diào)度器來啟動協(xié)程并且同時顯式指定?個命名:

launch(Dispatchers.Default + CoroutineName("test")) { println("I'm working in thread ${Thread.currentThread().name}") }

這段代碼使?了 -Dkotlinx.coroutines.debug JVM 參數(shù),輸出如下所?:

I'm working in thread DefaultDispatcher-worker-1 @test#2

協(xié)程作用域

讓我們將關(guān)于上下?,?協(xié)程以及作業(yè)的知識綜合在?起。假設(shè)我們的應(yīng)?程序擁有?個具有?命周期 的對象,但這個對象并不是?個協(xié)程。舉例來說,我們編寫了?個 Android 應(yīng)?程序并在 Android 的 activity 上下?中啟動了?組協(xié)程來使?異步操作拉取并更新數(shù)據(jù)以及執(zhí)?動畫等等。所有這些協(xié)程必 須在這個 activity 銷毀的時候取消以避免內(nèi)存泄漏。當(dāng)然,我們也可以?動操作上下?與作業(yè),以結(jié)合 activity 的?命周期與它的協(xié)程,但是 kotlinx.coroutines 提供了?個封裝:CoroutineScope 的 抽象。你應(yīng)該已經(jīng)熟悉了協(xié)程作?域,因為所有的協(xié)程構(gòu)建器都聲明為在它之上的擴(kuò)展。 我們通過創(chuàng)建?個 CoroutineScope 實例來管理協(xié)程的?命周期,并使它與 activit 的?命周期相關(guān) 聯(lián)。CoroutineScope 可以通過 CoroutineScope() 創(chuàng)建或者通過MainScope() ??函數(shù)。前者創(chuàng)建 了?個通?作?域,?后者為使? Dispatchers.Main 作為默認(rèn)調(diào)度器的 UI 應(yīng)?程序 創(chuàng)建作?域:

class Activity { private val mainScope = MainScope() fun destroy() { mainScope.cancel() } // 繼續(xù)運?…… // 在 Activity 類中 fun doSomething() { // 在?例中啟動了 10 個協(xié)程,且每個都?作了不同的時? repeat(10) { i -> mainScope.launch { delay((i + 1) * 200L) // 延遲 200 毫秒、400 毫秒、600 毫秒等等不同的時間 println("Coroutine $i is done") } } } } // Activity 類結(jié)束

在 main 函數(shù)中我們創(chuàng)建 activity,調(diào)?測試函數(shù) doSomething ,并且在 500 毫秒后銷毀這個 activity。這取消了從 doSomething 啟動的所有協(xié)程。我們可以觀察到這些是由于在銷毀之后,即使 我們再等?會?,activity 也不再打印消息。

val activity = Activity() activity.doSomething() // 運?測試函數(shù) println("Launched coroutines") delay(500L) // 延遲半秒鐘 println("Destroying activity!") activity.destroy() // 取消所有的協(xié)程 delay(1000) // 為了在視覺上確認(rèn)它們沒有?作

這個?例的輸出如下所?:

Launched coroutines Coroutine 0 is done Coroutine 1 is done Destroying activity!

你可以看到,只有前兩個協(xié)程打印了消息,?另?個協(xié)程在 Activity.destroy() 中單次調(diào)?了 job.cancel() 。

線程局部數(shù)據(jù)

有時,能夠?qū)?些線程局部數(shù)據(jù)傳遞到協(xié)程與協(xié)程之間是很?便的。然?,由于它們不受任何特定線程 的約束,如果?動完成,可能會導(dǎo)致出現(xiàn)樣板代碼。

ThreadLocal,asContextElement 擴(kuò)展函數(shù)在這?會充當(dāng)救兵。它創(chuàng)建了額外的上下?元素,且保 留給定 ThreadLocal 的值,并在每次協(xié)程切換其上下?時恢復(fù)它。             它很容易在下?的代碼中演?:

threadLocal.set("main") println("Pre-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") val job = launch(Dispatchers.Default + threadLocal.asContextElement(value = "launch")) { println("Launch start, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") yield() println("After yield, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'") } job.join() println("Post-main, current thread: ${Thread.currentThread()}, thread local value: '${threadLocal.get()}'")

在這個例?中我們使? Dispatchers.Default 在后臺線程池中啟動了?個新的協(xié)程,所以它?作在線程 池中的不同線程中,但它仍然具有線程局部變量的值,我們指定使? threadLocal.asContextElement(value = "launch") ,?論協(xié)程執(zhí)?在什么線程中都是沒有問題的。因此,其輸出如(調(diào)試)所?:

Pre-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main' Launch start, current thread: Thread[DefaultDispatcher-worker-1 @coroutine#2,5,main], thread local value: 'launch' After yield, current thread: Thread[DefaultDispatcher-worker-2 @coroutine#2,5,main], thread local value: 'launch' Post-main, current thread: Thread[main @coroutine#1,5,main], thread local value: 'main'

這很容易忘記去設(shè)置相應(yīng)的上下?元素。如果運?協(xié)程的線程不同,在協(xié)程中訪問的線程局部變量則可 能會產(chǎn)?意外的值。為了避免這種情況,建議使? ensurePresent ?法并且在不正確的使?時快速失 敗。 ThreadLocal 具有?流的?持,可以與任何 kotlinx.coroutines 提供的原語?起使?。但它有 ?個關(guān)鍵限制,即:當(dāng)?個線程局部變量變化時,則這個新值不會傳播給協(xié)程調(diào)?者(因為上下?元素? 法追蹤所有 ThreadLocal 對象訪問),并且下次掛起時更新的值將丟失。使? withContext 在協(xié)程 中更新線程局部變量,詳? asContextElement。 另外,?個值可以存儲在?個可變的域中,例如 class Counter(var i: Int) ,是的,反過來,可以 存儲在線程局部的變量中。然?,在這個案例中你完全有責(zé)任來進(jìn)?同步可能的對這個可變的域進(jìn)?的 并發(fā)的修改。 對于?級的使?,例如,那些在內(nèi)部使?線程局部傳遞數(shù)據(jù)的?于與?志記錄 MDC 集成,以及事務(wù)上下 ?或任何其它庫,請參?需要實現(xiàn)的 ThreadContextElement 接?的?檔。

文章題目:kotlin協(xié)程——>協(xié)程上下文與調(diào)度器-創(chuàng)新互聯(lián)
當(dāng)前鏈接:http://chinadenli.net/article36/ddihpg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)網(wǎng)站建設(shè)微信公眾號、網(wǎng)站內(nèi)鏈網(wǎng)站策劃、小程序開發(fā)網(wǎng)站導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司