[Kotlin/Native] 多線程怎么玩?

提到多線程鲤竹,寫慣了 Kotlin/JVM 的可能第一個(gè)反應(yīng)就是 thread { ... }拘泞,畢竟 Kotlin 已經(jīng)為我們設(shè)計(jì)好了很多東西疯汁,然而在 Kotlin/Native 上涕蚤,卻是不存在這樣的東西的歉糜。通常情況下乘寒, C 程序員會(huì)很熟悉 pthread,并且我們也可以在 Kotlin/Native 上實(shí)現(xiàn)類似的功能匪补。

由于 cinterop 的存在伞辛,使得我們可以直接調(diào)用 C 的標(biāo)準(zhǔn)函數(shù)庫,寫出來的 pthread 代碼是這樣的:

memScoped {
    val thread = alloc<pthread_tVar>()
    pthread_create(thread.ptr, null, staticCFunction { argc ->
        initRuntimeIfNeeded()
        ... ...
        null // as COpaquePointer?
    }, null)
    pthread_join(thread.value, null)
}

可能從上一篇文章起夯缺,大家就對類似于 pthread_tVar 或是 IntVar 這類寫法表示疑問蚤氏,這些類型是怎么來的呢,其實(shí)在 cinterop 擁有一種左值約定踊兜,我貼個(gè)原文大家看一下竿滨。

Also, any C type has the Kotlin type representing the lvalue of this type, i.e., the value located in memory rather than a simple immutable self-contained value. Think C++ references, as a similar concept. For structs (and typedefs to structs) this representation is the main one and has the same name as the struct itself, for Kotlin enums it is named ${type}Var, for CPointer<T> it is CPointerVar<T>, and for most other types it is ${type}Var.

總的來說,就是 C 類型后面加 Var 就是 Kotlin 內(nèi)的類型了捏境。

一般來說于游,只要是 C 可以實(shí)現(xiàn)的,可以用非常平滑的方法遷移到 Kotlin/Native垫言。


下面說一下 Kotlin/Native 原生實(shí)現(xiàn)的線程模型贰剥,相比于 pthread 的 API,原生實(shí)現(xiàn)的 Worker 更符合 Kotlin 的代碼習(xí)慣骏掀,也擁有更好的可讀性鸠澈。

如以下例子:

val str = "hello"
Worker.start().execute(TransferMode.SAFE, { str }) { it }.consume { println(it) }

這段代碼的意思很簡單,在 execute() 方法傳入?yún)?shù)并且啟動(dòng)生產(chǎn)截驮,該生產(chǎn)過程是異步的笑陈,完成后調(diào)用 consume() 進(jìn)行消費(fèi),消費(fèi)的過程是同步的葵袭。

這里會(huì)有一個(gè)需要非常注意的地方涵妥,不能偷懶,比如說以下代碼:

val str = "hello"
Worker.start().execute(TransferMode.SAFE, { }) { str }.consume { println(str) }

是不是看著沒問題? 但是實(shí)際編譯會(huì)報(bào)錯(cuò)坡锡,異常信息如下:

Worker.execute must take an unbound, non-capturing function or lambda

在這里需要注意的是蓬网,execute 方法的定義:

public final fun <T1, T2> execute(mode: TransferMode, producer: () -> T1, @VolatileLambda job: (T1) -> T2): Future<T2>

job 參數(shù)前有一個(gè) @VolatileLambda 的注解,這就表明了 job 所對應(yīng)的函數(shù)不允許有 綁定捕獲 的行為鹉勒,而直接傳入 str 變量顯然就是捕獲了帆锋。

所以必須在 producer 參數(shù)中予以傳參,知道這一點(diǎn)就可以做很多事情了禽额,比如說執(zhí)行一個(gè)外部的命令:

fun main(args: Array<String>) {
    if (args.isEmpty()) return
    val cmd = args[0]
    Worker.start().execute(TransferMode.SAFE, { cmd }) {
        runCommand(it)
    }.consume { 
        println("output => ${it.output}\nerror => ${it.error}") 
    }
}

其中 runCommand() 的代碼如下:

import kotlinx.cinterop.*
import platform.posix.*
data class CommandResult(val output: String, val error: String)
fun runCommand(cmd: String) = memScoped {
    var ret = ""
    var err = ""
    val size = 1024
    val buf = allocArray<ByteVar>(size)
    val fst = popen(cmd, "r")
    if (fst != null) {
        while (fgets(buf, size, fst) != null) {
            ret += buf.toKString()
        }
    } else {
        err = strerror(errno)?.toKString() ?: ""
    }
    pclose(fst)
    CommandResult(ret, err)
}

知道了這些之后锯厢,就可以愉快的玩轉(zhuǎn)多線程了皮官。順便,Kotlin 還主打協(xié)程实辑,在這里也一起提一下捺氢。

要使用協(xié)程,必須在 build.gradle 內(nèi)引用協(xié)程相關(guān)的庫:

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.1.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core-native:1.1.1'
}

這里要注意版本號(hào)剪撬,如果使用 Kotlin 1.3.21摄乒,那么協(xié)程庫版本號(hào)對應(yīng)為 1.1.1,如果使用 Kotlin 1.3.31残黑,則對應(yīng)為 1.2.1馍佑。如果版本號(hào)不匹配,會(huì)引起編譯異常梨水。

然后我們需要自己定義一個(gè) CoroutineScope挤茄,這里只是做演示用,在這個(gè) Scope 內(nèi)不做其他事冰木,可以寫成這樣:

private class MyScope: CoroutineScope {
    private val dispatcher = object : CoroutineDispatcher() {
        override fun dispatch(context: CoroutineContext, block: Runnable) {
            block.run()
        }
    }
    private val job = Job()
    override val coroutineContext: CoroutineContext get() = dispatcher + job
}

然后上面的 runCommand() 代碼就可以改成這樣:

MyScope().launch {
    val ret = runCommand(cmd)
    println("output => ${ret.output}\nerror => ${ret.error}")
}

順便一提,原先定義的 runCommand 是一個(gè)常規(guī)方法笼恰,在協(xié)程里用也可以將其改為 suspend 方法:

suspend fun runCommand(cmd: String): CommandResult { ... }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末踊沸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子社证,更是在濱河造成了極大的恐慌逼龟,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件追葡,死亡現(xiàn)場離奇詭異腺律,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)宜肉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進(jìn)店門匀钧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谬返,你說我怎么就攤上這事之斯。” “怎么了遣铝?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵佑刷,是天一觀的道長。 經(jīng)常有香客問我酿炸,道長瘫絮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任填硕,我火速辦了婚禮麦萤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己频鉴,他們只是感情好栓辜,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著垛孔,像睡著了一般藕甩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上周荐,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天狭莱,我揣著相機(jī)與錄音,去河邊找鬼概作。 笑死腋妙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讯榕。 我是一名探鬼主播骤素,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼愚屁!你這毒婦竟也來了济竹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤霎槐,失蹤者是張志新(化名)和其女友劉穎送浊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丘跌,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡袭景,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闭树。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耸棒。...
    茶點(diǎn)故事閱讀 40,742評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蔼啦,靈堂內(nèi)的尸體忽然破棺而出榆纽,到底是詐尸還是另有隱情,我是刑警寧澤捏肢,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布奈籽,位于F島的核電站,受9級(jí)特大地震影響鸵赫,放射性物質(zhì)發(fā)生泄漏衣屏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一辩棒、第九天 我趴在偏房一處隱蔽的房頂上張望狼忱。 院中可真熱鬧膨疏,春花似錦、人聲如沸钻弄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窘俺。三九已至饲帅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘤泪,已是汗流浹背灶泵。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留对途,地道東北人赦邻。 一個(gè)月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像实檀,于是被迫代替她去往敵國和親惶洲。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內(nèi)容