Swift并发中inout self错误处理指南

Viewed 0

概览

在 Swift 的结构化并发代码中,编译器经常会报告错误:mutable capture of 'inout' parameter 'self' is not allowed in concurrently-executing code。然而,在某些特定情况下,这一错误实际上并不会导致问题。那么,当这些“伪”错误出现时,我们该如何让编译器满意并确保代码顺利执行呢?

本文将介绍以下内容:

  • 1. 编译器恼人的抱怨
  • 2. 什么情况下这不是一个问题?
  • 3. 规避之道
  • 4. 实际示例

让我们开始吧!

1. 编译器恼人的抱怨

首先,查看引发问题的代码:

struct Foo {
    var name = ""

    private func spawnRandNameNumber() -> Int {
        Int.random(in: 0...10000)
    }

    mutating func dealName() async {
        await withTaskGroup(of: Void.self) { group in
            group.addTask {
                name += "\(spawnRandNameNumber())"
            }

            group.addTask {
                name += "\(spawnRandNameNumber())"
            }
        }
    }
}

在这段代码中,我们使用结构化并发中的任务组(TaskGroup)尝试同时修改对象的 name 属性。编译时,编译器会在每处修改 name 属性的代码行报错,指出不允许在并发执行代码中可变捕获 inout 参数 self

根本原因在于,我们试图并发修改 self 中同一内存位置的值,而 self 此时被 inout 修饰。因此,编译器的抱怨是合理且有依据的。但这是否在所有情况下都是错误呢?并非如此。

2. 什么情况下这不是一个问题?

假设我们尝试同时修改 self 中两个完全独立的不同属性呢?下面稍作修改:

struct Foo {
    var name = ""
    var age = 11

    private func spawnRandNameNumber() -> Int {
        Int.random(in: 0...10000)
    }

    private func spawnRandAge() -> Int {
        Int.random(in: 1...121)
    }

    mutating func dealName() async {
        await withTaskGroup(of: Void.self) { group in
            group.addTask {
                name += "\(spawnRandNameNumber())"
            }

            group.addTask {
                age = spawnRandAge()
            }
        }
    }
}
0 Answers