1. 程式人生 > >Kotlin學習(十九)—— 攜程

Kotlin學習(十九)—— 攜程

協程的引入

注意:kotlin中的協程在1.1中還是實驗性的(小編理解為,先不要用)
⼀些 API 啟動⻓時間運⾏的操作(例如⽹絡 IO、⽂件 IO、CPU 或 GPU 密集型任務等),並要求調⽤者阻塞直到它們完成。協程提供了⼀種避免阻塞執行緒並用更廉價、更可控的操作替代執行緒阻塞的⽅法:協程掛起

協程通過將複雜性放⼊庫來簡化非同步程式設計。程式的邏輯可以在協程中順序地表達,而底層庫會為我們解決其非同步性。該庫可以將使用者程式碼的相關部分包裝為回撥、訂閱相關事件、在不同執行緒(甚至不同機器!)上排程執行,而程式碼則保持如同順序執行⼀樣簡單
上面的語言小編理解為:
1. 我們程式設計要實現攜程,kotlin已經把實現的複雜性轉移給一個底層的庫
2. 我們實現就像我們寫一個Java的同步方法一樣,使用一個關鍵字標記就好
3. 底層的庫會幫我們實現協程的排程

許多在其他語言中可用的非同步機制可以使⽤ Kotlin 協程實現為庫。這包括源於 C# 和 ECMAScript 的 async/await、源於 Go 的 管道 和 select 以及源於 C# 和 Python 生成器/yield。關於提供這些結構的庫請參⻅其下⽂描述。

阻塞 vs 掛起

基本上,協程計算可以被掛起而無需阻塞執行緒。執行緒阻塞的代價通常是昂貴的,尤其在高負載時,因為只有相對少量執行緒實際可用,因此阻塞其中⼀個會導致⼀些重要的任務被延遲。

另⼀方面,協程掛起幾乎是無代價的。不需要上下文切換或者 OS 的任何其他干預。最重要的是,掛起可以在很大程度上由使用者庫控制:作為庫的作者,我們可以決定掛起時發生什麼並根據需求優化/記日誌/截獲。

另⼀個區別是,協程不能在隨機的指令中掛起,而只能在所謂的掛起點掛起,這會呼叫特別標記的函式。
get一下上面的資訊:
1. 協程不阻塞,執行緒會有阻塞狀態(Java的執行緒在等待同步鎖,IO,或者呼叫sleep()方法後等方式進入阻塞,阻塞後的執行緒有要進入就緒狀態才能喲執行的機會,這繁瑣的呼叫方式可能會讓任務延遲),而協程是計算可以被掛起(不明白這句話,什麼是可以計算被掛起)。
2. 協程不需要上下文切換或者 OS 的任何其他干預,而且使用者的控制權大大加大。

掛起函式

當我們呼叫標記有特殊修飾符 suspend 的函式時,會發生掛起(協程呼叫可能比較麻煩)
如果我們呼叫協程,要引入kotlin庫:
maven依賴:

<dependency>
            <groupId>org.jetbrains.kotlinx</groupId>
            <artifactId>kotlinx-coroutines-core</artifactId>
            <version>0.22.5</version>
</dependency>

我們不能直接呼叫suspend修飾的方法:
只能從協程和其他掛起函式中呼叫。(也就是說,suspend修飾的函式,只能被suspend修飾的函式呼叫)

import kotlinx.coroutines.experimental.async

suspend  fun  doSomething(i : Int):Int{
    println("會被掛起的函式執行了")
    return 1
}

fun main(args: Array<String>) {
  async {  //suspend修飾的很熟
      doSomething(1)
  }
}

注意:上面程式碼中的async函式是在kotlinx.coroutines.experimental中的
這樣的函式(doSomething())稱為掛起函式,因為呼叫它們可能掛起協程(如果相關呼叫的結果已經可用,庫可以決定繼續進行而不掛起)。掛起函式能夠以與普通函式相同的方式獲取引數和返回值,但它們只能從協程和其他掛起函式中呼叫。事實上,要啟動協程,必須至少有⼀個掛起函式,它通常是匿名的(即它是⼀個掛起lambda 表示式)。讓我們來看⼀個例⼦,⼀個簡化的 async() 函式(源自kotlinx.coroutines 庫):
下面貼上async的程式碼:

public fun <T> async(context: kotlin.coroutines.experimental.CoroutineContext,
                     start: kotlinx.coroutines.experimental.CoroutineStart,
                     parent: kotlinx.coroutines.experimental.Job?,
                     block: suspend kotlinx.coroutines.experimental.CoroutineScope.() -> T): kotlinx.coroutines.experimental.Deferred<T> {
                     }

這⾥的 async() 是⼀個普通函式(不是掛起函式),但是它的 block 引數具有⼀個帶 suspend 修飾符的函式型別:suspend kotlinx.coroutines.experimental.CoroutineScope.() -> T 。所以,當我們將⼀個 lambda 表示式傳給 async() 時,它會是掛起 lambda 表示式,於是我們可以從中呼叫掛起函式:

繼續該類比,await() 可以是⼀個掛起函式(因此也可以在⼀個 async {} 塊中呼叫),該函式掛起⼀個協程,直到⼀些計算完成並返回其結果:

async {
    ……
    val result = computation.await()
    ……
}

請注意,掛起函式 await() 和 doSomething() 不能在像 main() 這樣的普通函式中調⽤:(能作為suspend的引數傳入,lambda表示式的函式體中使用)

fun main(args: Array<String>) {
    doSomething() // 錯誤:掛起函式從⾮協程上下⽂調⽤
}

還要注意的是,掛起函式可以是虛擬的,當覆蓋它們時,必須指定 suspend 修飾符:

interface Base {
    suspend fun foo()
} 
class Derived: Base {
    override suspend fun foo() { …… }
}

@RestrictsSuspension 註解

擴充套件函式(和 lambda 表示式)也可以標記為 suspend ,就像普通的⼀樣。這允許建立 DSL 及其他使用者可擴充套件的 API。在某些情況下,庫作者需要阻止使用者新增新方式來掛起協程。
為了實現這⼀點,可以使⽤ @RestrictsSuspension 註解。當接收者類/介面 R 用它標註時,所有掛起擴充套件都需要委託給 R 的成員或其它委託給它的擴充套件。由於擴充套件不能無限相互委託(程式不會終止),這保證所有掛起都通過調⽤ R 的成員發生,庫的作者就可以完全控制了。
這在少數情況是需要的,當每次掛起在庫中以特殊方式處理時。例如,當通過 buildSequence() 函式實現下⽂所述的生成器時,我們需要確保在協程中的任何掛起呼叫終呼叫用yield() 或 yieldAll() 而不是任何其他函式。這就是為什麼 SequenceBuilder 用 @RestrictsSuspension 註解:
參⻅其 Github 上 的原始碼。

@RestrictsSuspension
public abstract class SequenceBuilder<in T> {
    ……
}

協程的內部機制

我們不是在這⾥給出⼀個關於協程如何⼯作的完整解釋,然⽽粗略地認識發⽣了什麼是相當重要的。
協程完全通過編譯技術實現(不需要來⾃ VM 或 OS 端的⽀持),掛起通過程式碼來⽣效。基本上,每個掛起函式(優化可能適⽤,但我們不在這⾥討論)都轉換為狀態機,其中的狀態對應於掛起調⽤。剛好在掛起前,下⼀狀態與相關區域性變數等⼀起儲存在編譯器⽣成的類的欄位中。在恢復該協程時,恢復區域性變數並且狀態機從剛好掛起之後的狀態進⾏。
掛起的協程可以作為保持其掛起狀態與區域性變數的物件來儲存和傳遞。這種物件的型別是 Continuation ,⽽這⾥描述的整個程式碼轉換對應於經典的延續性傳遞⻛格(Continuation-passing style)。因此,掛起函式有⼀個 Continuation 型別的額外引數作為⾼級選項。
關於協程⼯作原理的更多細節可以在這個設計⽂檔中找到。在其他語⾔(如 C# 或者 ECMAScript 2016)中的 async/await 的類似描述與此相關,雖然它們實現的語⾔功能可能不像 Kotlin 協程這樣通⽤。

協程是實驗性的(小編這裡先大致瞭解,今後再探究)

協程的設計是實驗性的,這意味著它可能在即將釋出的版本中更改。當在 Kotlin 1.1 中編譯協程時,預設情況下會報⼀個警告: “協程”功能是實驗性的。要移出該警告,你需要指定 opt-in 標誌。
由於其實驗性狀態,標準庫中協程相關的 API 放在 kotlin.coroutines.experimental 包下。當設計完成並且實驗性狀態解除時,最終的 API 會移動到 kotlin.coroutines ,並且實驗包會被保留(可能在⼀個單獨的構件中)以實現向後相容。
重要注意事項:我們建議庫作者遵循相同慣例:給暴露基於協程 API 的包新增“experimental”字尾(如 com.example.experimental ),以使你的庫保持⼆進位制相容。當最終 API 釋出時,請按照下列步驟操作:
將所有 API 複製到 com.example(沒有 experimental 字尾),保持實驗包的向後相容性。這將最⼩化你的⽤⼾的遷移問題。

相關推薦

Kotlin學習——

協程的引入 注意:kotlin中的協程在1.1中還是實驗性的(小編理解為,先不要用) ⼀些 API 啟動⻓時間運⾏的操作(例如⽹絡 IO、⽂件 IO、CPU 或 GPU 密集型任務等),並要求調⽤者阻塞直到它們完成。協程提供了⼀種避免阻塞執行緒並用更廉價、

C++語言學習——C++類型識別

c++類 面向 字節 檢查 case include 指向 指針和引用 peid C++語言學習(十九)——C++類型識別 一、C++類型識別簡介 1、C++類型識別簡介 C++是靜態類型語言,其數據類型是在編譯期就確定的,不能在運行時更改。C++語言中,靜態類型是對象自身

GO語言學習Go 錯誤處理

錯誤處理 math println 接口 class int 處理機 nil sprintf Go 錯誤處理 Go 語言通過內置的錯誤接口提供了非常簡單的錯誤處理機制。 error類型是一個接口類型,這是它的定義: type error interface {

深度學習基於空間金字塔池化的卷積神經網路物體檢測

原文地址:http://blog.csdn.net/hjimce/article/details/50187655 作者:hjimce 一、相關理論    本篇博文主要講解大神何凱明2014年的paper:《Spatial Pyramid Pooling in Dee

機器學習之python學習

今天來學習python中的檔案操作這是很關鍵的, 這次先學習檔案的讀寫,需要注意的是檔案的許可權的問題。 總結如下最常用的許可權: 許可權 數量 r 開啟只讀檔案,該檔案必須存在。

JMeter學習JMeter測試MongoDB

JMeter測試MongoDB效能有兩種方式,一種是利用JMeter直接進行測試MongoDB,還有一種是寫Java程式碼方式測試MongoDB效能。   第一種方法 1.編寫Java程式碼,內容如下: package com.test.mongodb; im

opencv學習:Canny邊緣檢測

參考學習連結:https://www.cnblogs.com/mightycode/p/6394810.html 程式碼如下: #匯入cv模組 import cv2 as cv import numpy as np #Canny邊緣提取 def edge_demo(image):

spring深入學習 IOC 之 Factory 例項化 bean

這篇我們關注建立 bean 過程中的第一個步驟:例項化 bean,對應的方法為:createBeanInstance(),如下: protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd

Spring 學習——重用切入點表示式

•在編寫 AspectJ 切面時, 可以直接在通知註解中書寫切入點表示式. 但同一個切點表示式可能會在多個通知中重複出現. •在 AspectJ 切面中, 可以通過 @Pointcut 註解將一個切入點宣告成簡單的方法. 切入點的方法體通常是空的, 因為將切入點定義與應用程式邏輯混在一起是不合理

dart語言學習— 閉包

含義 閉包是一個方法(物件) 閉包定義在其他方法內部 閉包能夠訪問外部方法內的區域性變數,並持有其狀態 案例 void main(List<String> args) { var func = a(); for (var i = 0;

機器學習——拉普拉斯光順Laplace smoothing

我們已經描述過的樸素貝葉斯演算法能夠很好地解決許多問題,但是有一個簡單的改變使得它更好地工作,特別是對於文字分類。讓我們簡單地討論演算法在當前形式下的問題,然後討論如何修復它。考慮垃圾郵件/電子郵件分類,讓我們假設在完成CS229並完成了對專案的出色工作之後,您決定在2003

機器學習——PageRank演算法, KNN, loss function詳解

PageRank演算法 概述 在PageRank提出之前,已經有研究者提出利用網頁的入鏈數量來進行連結分析計算,這種入鏈方法假設一個網頁的入鏈越多,則該網頁越重要。早期的很多搜尋引擎也採納了入鏈數量作為連結分析方法,對於搜尋引擎效果提升也有較明顯

ROS學習--使用Gazebo模擬器

Stage模擬器是模擬二維平面的模擬器,而Gazebo則是三維的,可以自己在地圖上新增幾何體。 先按前面的教程做完,安裝了turltebot,啟動Gazebo模擬器 $ roslaunch turtlebot_gazebo turtlebot_world

軟體設計模式學習直譯器模式

> 直譯器是一種不常使用的設計模式,它用於描述如何構成一個簡單的語言直譯器,主要應用於使用面嚮物件語言開發的編譯器和直譯器設計。當我們需要開發一個新的語言時,可以考慮使用直譯器模式 ## 模式動機 如果在系統中某一特定型別的問題發生的頻率很高,此時可以考慮將這些問題的例項表述為一個語言中的句子。再

python學習筆記面向對象編,類

時代 alt 類名 rst tps 玉溪 connect nbsp nco 一、面向對象編程 面向對象,是一種程序設計思想。 編程範式:編程範式就是你按照什麽方式去編程,去實現一個功能。不同的編程範式本質上代表對各種類型的任務采取的不同的解決問題的思路,兩種最重要的編程範式

快速學習Kotlin

協程是什麼? 從本質上來講,協程就是一個輕量級的執行緒。執行緒是由系統(語言系統或者作業系統)進行排程的,切換時有著一定的開銷。而協程,它的切換由程式自己來控制,無論是 CPU 的消耗還是記憶體的消耗都大大降低。 在協程中某段程式碼是可以暫停的,這時候可以去轉而執行另一段

聊聊高並發理解並發編的幾種&quot;性&quot; -- 可見性,有序性,原子性

sock clas 關註 條件 infoq zed 應該 單獨 ssa 這篇的主題本應該放在最初的幾篇。討論的是並發編程最基礎的幾個核心概念。可是這幾個概念又牽扯到非常多的實際技術。比方Java內存模型。各種鎖的實現,volatile的實現。原子變量等等,每個都可以展開

Python學習筆記

插入 imp 集合類 屬性 counter 以及 雙向 ror 簡單的 一、collections介紹   collections是Python中內建的一個集合模塊,提供了許多有用的集合類 二、namedtuple   namedtuple是一個函數,用來創建一個類似類的自

ShaderLab學習小結RenderToCubemap創建能反射周圍環境的效果

隨著 思路 edi 繼續 size material cubemap 緩沖區 方法 繼續用“ShaderLab學習小結(十八)cubemap”中所做的shader想要讓它能對周圍的環境進行反射思路就是要讓它的cubemap裏的內容是周圍環境的映射shader不變,就要想辦法

Linux學習筆記文件壓縮

文件壓縮一、常見的壓縮文件 Windows .rar .zip .7z Linux .zip,.gz,.bz2,.xz,.tar.gz,.tar.bz2,.tar.xz文件壓縮可以節省內存,也可以節省傳輸速度 二、gzip首先創建了一個文件夾 /tmp/d6z/找了些比較大的文件寫入1.txt例如find