运行下面的代码,我得到“从错误的线程访问领域”。第二行
try! realm.write({
发生错误,第一行写入不会导致错误。知道如何解决它吗?
let realm = try! await Realm()
print("User Realm User file location: \(realm.configuration.fileURL!.path)")
try! realm.write { // <= No error here
realm.add(groups, update: .modified)
}
StartApp._Groups = groups
if let items = await api.getArticles(aricleIDs: ids) {
try! realm.write({ // <= Error here
realm.add(items, update: .modified)
})
StartApp._Items = items
var index = 0
StartApp._Items = StartApp.Items.map { item in
item.i = index
index = index + 1
return item
}
groups.forEach { group in
group.items = items.filter({ $0.groupId == group.id })
}
}
我修复了下面的问题,删除了await Realm()并将写入合并到一个
DispatchQueue.main.async{
do {
let realm = try Realm()
var index = 0
items.forEach { item in
item.i = index
index = index + 1
}
try realm.write({
realm.add(groups, update: .modified)
realm.add(items, update: .modified)
}
})
} catch {
print("Realm error: \(error)")
}
}
问题是在初始化 Realm 时使用
await
,这基本上是说在其他异步上下文上实例化领域并返回。即使可以,也没有必要。领域初始值设定项不是异步的。
要求领域在其实例化的同一异步上下文(线程)上完成事务。 通过使用
withCheckedContinuation
函数(还有一个抛出函数 withCheckedThrowingContinuation
)将实例化和事务包装在代码块中,可以通过异步等待来确保这一点。阅读更多这里和这里
它的外观如下:
public func loadSomething(with id: String) async throws -> Something {
let result = try await withCheckedThrowingContinuation { continuation in
do {
let realm = try Realm()
guard let something = realm.object(ofType: DBSomething.self, forPrimaryKey: id) else {
throw YourError.somethingHappened
}
continuation.resume(with: something)
} catch {
continuation.resume(with: .failure(error))
}
}
return result
}
在处理并发和领域时,您应该小心线程,因为领域实例不是线程安全的,您不能简单地将其传递给其他异步函数或后台完成闭包等。
更多的是,在调用其他异步函数后,可以随时在异步函数内更改当前线程,例如:
static func run() async throws {
// Thread X
let realm = try await Realm()
// Thread X
getObjectsCallback { objects in // Callback can be run on an other thread!
// Thread Y
try realm.write { // Error: Realm accessed from incorrect thread.
realm.add(objects, update: .modified)
}
}
// Thread X
let objects = await getObjectsAsync() // Thread can be changed on return!
// Thread Z
try realm.write { // Error: Realm accessed from incorrect thread.
realm.add(objects, update: .modified)
}
}
最简单的解决方案是通过
Actor
同步访问,您可以简单地使用在主线程上运行的 @MainActor
或创建自己的:
@globalActor
actor RealmActor: GlobalActor {
static let shared = RealmActor()
}
然后可以按照以下方式重写上面的代码来解决问题:
@RealmActor
static func run() async throws {
// Thread X
let realm = try await Realm(actor: RealmActor.shared)
// Thread X
getObjectsCallback { objects in
Task { @RealmActor in
// Thread X
try realm.write {
realm.add(objects, update: .modified)
}
}
}
// Thread X
let objects = await getObjectsAsync()
// Thread X
try realm.write {
realm.add(objects, update: .modified)
}
}