Multithreading 在Kotlin Native中,如何将对象保留在单独的线程中,并在不使用C指针的情况下改变其状态?
我正在探索Kotlin Native,有一个程序,其中有一堆并发的东西 (在Windows上运行,但这是一个一般性问题) 现在,我想添加简单的日志记录。一种组件,通过将字符串作为新行追加到一个文件中来记录字符串,该文件在“追加”模式下保持打开状态 (理想情况下,我只需要一个“全局”函数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关于在线程之间传递对象的规
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}")
}
}