使用协程时从错误的线程访问Realm。

8
我正在尝试使用Kotlin协程结合Realm,使用withContext()在新线程中进行查询。
我观察到的是,在循环中线程会切换,导致Realm抛出以下异常:在错误的线程中访问Realm。Realm对象只能在创建它们的线程上访问。
withContext(Dispatchers.IO) {
            val realm = Realm.getDefaultInstance()
            val images = mutableListOf<String>("id1", "id2", ...)
            for (imageId in images) {
                 println("THREAD : ${Thread.currentThread().name}")
                 val image = realm.where<Image>().equalTo("imageId", imageId).findFirst()
                 delay(1000) // Can lead to an actual switching to another thread
            }
            realm.close()
}

作为 dispatchers.IO 文档在此提到:https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-dispatchers/-i-o.html
该分发器与 [Default][Dispatchers.Default] 分发器共享线程,因此使用 withContext(Dispatchers.IO) { ... } 不会导致实际切换到另一个线程;通常执行将继续在同一线程中进行。
我不明白为什么线程在循环中切换。 如何正确地使用协程管理 Realm 实例?

我改成了val,但结果还是一样。我尝试打印当前线程并得到:THREAD: DefaultDispatcher-worker-4 THREAD: DefaultDispatcher-worker-5 - CuirMoustachu
是的,我这里没有完整的代码,但是失败的部分就是这一段,它失败的那一行是"val image = realm.where<Image>().equalTo("imageId", imageId).findFirst()"。我知道为什么它会失败,因为它在循环内部切换了线程,但是为什么会发生这种行为呢? - CuirMoustachu
你的代码中的 images 是什么?它不是你在其他地方创建的 Realm 对象的集合,现在正在从另一个线程中访问吗? - Marko Topolnik
图片只是字符串列表。这个异常是因为我在循环内部有一个挂起调用,导致线程切换到另一个线程。那么我应该每次在循环内打开一个 Realm 实例吗?这不是很不好吗? - CuirMoustachu
2
也许我应该使用Executors.newSingleThreadExecutor().asCoroutineDispatcher()而不是Dispatchers.IO? - CuirMoustachu
显示剩余5条评论
2个回答

8
你可以在协程中创建另一个新的单线程来运行Realm。例如:
val dispatcher =  Executors.newSingleThreadExecutor().asCoroutineDispatcher()
jobs.launch(dispatcher) {
  // create new Realm instance
}

7

每次协程被暂停时,在恢复时,调度程序会为其找到一个线程来运行。很可能它将与先前运行的线程不同。


在 withContext 中调用的函数是阻塞的,如果在主线程上运行,则可能会使主线程跳过某些帧。但它运行的线程仍会发生改变。 - CuirMoustachu
1
它可能会在每次调用之间发生更改,而不是在单个调用中。或者,在 withContext 中您有一个暂停调用。 - Marko Topolnik
你说得对,我在循环内部有一个挂起的调用,这就解释了为什么。抱歉,我编辑了我的代码以使我的问题更清晰。 - CuirMoustachu
18
我们现在知道了这种情况发生的原因,但是否已经有了在协程中使用 Realm 的最佳实践? - hardysim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接