在并发执行的代码中捕获变量'value'的突变

3

我在我的函数中使用了async await,但是在使用时出现了错误。

我的代码如下:

class DatabaseManager {

   func storeValOnDB(value: String) async -> Bool {

     do {
            
            let finish = try await self.database.child("Values").setValue(value)
            print(finish)
            return true
        } catch {
            print(error.localizedDescription)
            return false
        }
    }
}

我在我的视图模型函数中使用了这个:

func storeValue() {

    var foo = ""

    Task {
   
         let result = await DatabaseManager.shared.storeValOnDB(value: self.value)

         switch result {
                            
           case true:
                            
              foo = value  // Mutation of captured var 'convos' in concurrently-executing code
           case false:
               print("failed to store value")
        }
    }
}

当我在任务中给一些值进行分配,但却出现错误。


请查看以下链接:https://stackoverflow.com/help/how-to-ask,当然也要展示一个最小化的代码示例:https://stackoverflow.com/help/minimal-reproducible-example。 - workingdog support Ukraine
你的Task内的代码是异步执行的,你试图从该异步块外部捕获局部变量foo。这是不允许的。foo = value赋值何时发生是不确定的,这取决于数据库操作完成所需的时间。如果允许赋值,那么在应用的不同执行中,由于异步工作完成的时间不同,可能会得到不同的结果。也许[编辑]一下你的问题,以解释你想要做什么。可能你只需要把代码移到Task中。 - Paulw11
1个回答

1

在任务声明之外,不应访问可变值。如果需要从任务中返回数据,可以使用任务的返回值。

为了让任务返回一个值,在创建任务时需要指定。因此,storeValue函数可以重构为:

func storeValue() async {
    var foo = ""
    var valueToStore = "value"
    let t = Task<String, Never> { [valueToStore] in
        let result = await DatabaseManager.shared.storeValOnDB(value: valueToStore)
        switch result {
        case true:
            return "some new value \(foo)"
        case false:
            print("failed to store value")
            return ""
        }
    }
    foo = await t.value
}

在上述代码中,Task<String, Never> 表示任务返回一个字符串且不会抛出错误。请注意,我们不再使用 task 中的 self.value,而是使用捕获列表并定义 [valueToStore],以便将 valueToStore 的值复制到任务中。在这种情况下,我们避免了在多个线程中访问/修改变量时可能出现的任何内存问题。
此外,您应该使用错误抛出机制来代替在 storeValOnDB 函数中返回布尔值。假设 storeValOnDB 的定义如下:
func storeValOnDB(value: String) async throws

因此,如果无法存储该值,它将抛出错误,否则它不会返回任何值。这极大地简化了我们的代码。现在,任务代码变得更短且易于阅读,我们也可以省略可选的泛型声明(<>):

func storeValue() async {
    var foo = ""
    var valueToStore = "value"
    let t = Task { [valueToStore] in
        try await DatabaseManager.shared.storeValOnDB(value: valueToStore)
        return "some new value"
    }
    do {
        foo = try await t.value
    } catch {
        print("failed to store value: \(error)")
    }
}

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