Multithreading 在Kotlin Native中,如何将对象保留在单独的线程中,并在不使用C指针的情况下改变其状态?

Multithreading 在Kotlin Native中,如何将对象保留在单独的线程中,并在不使用C指针的情况下改变其状态?,multithreading,kotlin,kotlin-multiplatform,kotlin-native,Multithreading,Kotlin,Kotlin Multiplatform,Kotlin Native,我正在探索Kotlin Native,有一个程序,其中有一堆并发的东西 (在Windows上运行,但这是一个一般性问题) 现在,我想添加简单的日志记录。一种组件,通过将字符串作为新行追加到一个文件中来记录字符串,该文件在“追加”模式下保持打开状态 (理想情况下,我只需要一个“全局”函数 fun log(text:String) {...} ] …我可以从任何地方调用,包括从“内部”其他工作人员调用,这很有效。这里的含义是这样做并不简单,因为Kotlin Native关于在线程之间传递对象的规

我正在探索Kotlin Native,有一个程序,其中有一堆并发的东西 (在Windows上运行,但这是一个一般性问题)

现在,我想添加简单的日志记录。一种组件,通过将字符串作为新行追加到一个文件中来记录字符串,该文件在“追加”模式下保持打开状态

(理想情况下,我只需要一个“全局”函数

fun log(text:String) {...} ] 
…我可以从任何地方调用,包括从“内部”其他工作人员调用,这很有效。这里的含义是这样做并不简单,因为Kotlin Native关于在线程之间传递对象的规则(TLDR:您不应该传递可变对象。请参阅:)。 此外,我的日志函数理想情况下可以接受任何对象。)


我提出的解决方案包括:

首先,我创建一个分离的记录器对象

val loggerGraph = DetachedObjectGraph { FileLogger("/foo/mylogfile.txt")}
然后使用
loggerGraph.asCPointer()
()获取一个
copaqueinter
到分离的图形:

val myPointer = loggerGraph.asCPointer()
现在我可以将这个指针传递到Worker(通过Worker的),并在那里使用它。或者我可以将指针存储在全局变量中


对于写入文件的代码,每当我想记录一行时,我必须再次从指针创建一个
DetachedObjectGraph
对象, 和
attach()
it以获取对我的fileLogger对象的引用:

val fileLogger = DetachedObjectGraph(myPointer).attach()
现在,我可以在记录器上调用日志函数:

fileLogger.log("My log message")
这就是我在Kotlin Native中提供的用于并发的API(从Kotlin 1.3.61开始), 但是我想知道更好的方法是什么(使用Kotlin,而不是使用C)。显然,为写入的每一行创建一个DetachedObjectGraph对象是不好的。


我们可以用更一般的方式提出这个问题:如何在单独的线程(或工作线程)中保持可变资源的打开状态,并向其发送消息


旁白:真正使用线程可以解决这个问题,但问题是如何使用当前可用的API(Kotlin 1.3.61)来解决这个任务。

您所采取的方法非常正确,而且应该是这样做的


我要补充的是,不要到处传递指针。您应该传递一个冻结的
文件记录器
,它将在内部保存对
AtomicRef
的引用,连接和分离应该在内部完成。特别是因为
DetachedObjectGraph
s一旦附加就无效。

您绝对不应该以问题中提出的方式使用
DetachedObjectGraph
。没有什么可以阻止您尝试连接多个线程,或者如果您传递同一个指针,尝试连接到一个又一个连接到它的无效线程

正如Dominic所提到的,您可以将
DetachedObjectGraph
保存在
AtomicReference
中。但是,如果要在
AtomicReference
中保留
DetachedObjectGraph
,请确保类型为
AtomicRef
,并且在
DetachedObjectGraph
为空时执行繁忙循环。这将防止多个线程使用相同的
DetachedObjectGraph
。确保将其设置为null,并以原子方式重新填充它

但是,
FileLogger
是否需要可变?如果你正在写一个文件,它似乎不是这样。即使如此,我也会将可变对象隔离到一个单独的辅助对象,并向其发送日志消息,而不是在AtomicRef中执行
DetachedObjectGraph

根据我的经验,
DetachedObjectGraph
在生产代码中非常少见。我们现在不在任何地方使用它

要将可变状态隔离到
工作者
,如下所示:


class MutableThing<T:Any>(private val worker:Worker = Worker.start(), producer:()->T){
    private val arStable = AtomicReference<StableRef<T>?>(null)
    init {
        worker.execute(TransferMode.SAFE, {Pair(arStable, producer).freeze()}){
            it.first.value = StableRef.create(it.second()).freeze()
        }
    }
    fun <R> access(block:(T)->R):R{
        return worker.execute(TransferMode.SAFE, {Pair(arStable, block).freeze()}){
            it.second(it.first.value!!.get())
        }.result
    }
}

object Log{
    private val fileLogger = MutableThing { FileLogger() }

    fun log(s:String){
        fileLogger.access { fl -> fl.log(s) }
    }
}

class FileLogger{
    fun log(s:String){}
}

实现了一个更正式的版本,附带了相关的博客帖子:酷。让我很高兴的是,我一直在烦你,最后你写了这篇文章,对大家都有帮助!谢谢
@Test
fun goIso(){
    val mt = MutableThing { mutableListOf("a", "b")}
    val workers = Array(4){Worker.start()}
    val futures = mutableListOf<Future<*>>()
    repeat(1000) { rcount ->
        val future = workers[rcount % workers.size].execute(
            TransferMode.SAFE,
            { Pair(mt, rcount).freeze() }
        ) { pair ->
            pair.first.access {
                val element = "ttt ${pair.second}"
                println(element)
                it.add(element)
            }
        }
        futures.add(future)
    }

    futures.forEach { it.result }

    workers.forEach { it.requestTermination() }

    mt.access {
        println("size: ${it.size}")
    }
}