使用异步和等待从错误的线程访问领域

问题描述 投票:0回答:3

运行下面的代码,我得到“从错误的线程访问领域”。第二行

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 })
                            }
                        }
swift async-await realm
3个回答
2
投票

我修复了下面的问题,删除了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)")
                        }
                    }

2
投票

问题是在初始化 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
    }


0
投票

在处理并发和领域时,您应该小心线程,因为领域实例不是线程安全的,您不能简单地将其传递给其他异步函数或后台完成闭包等。

更多的是,在调用其他异步函数后,可以随时在异步函数内更改当前线程,例如:

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)
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.