标签: swift-concurrency

Swift wait/async - 如何同步等待异步任务完成?

我正在 Swift 中桥接同步/异步世界,并逐步采用异步/等待。我正在尝试调用一个从非异步函数返回值的异步函数。我知道显式使用Task是执行此操作的方法,例如,如此处所述

该示例并不真正适合,因为该任务不返回值。

经过多次搜索,我无法找到任何我认为很常见的问题的描述:异步任务的同步调用(是的,我知道这可能会冻结主线程)。

理论上我想在同步函数中编写的是:

let x = Task {
  return await someAsyncFunction()
}.result
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试这样做时,由于尝试访问而出现此编译器错误result

'async' property access in a function that does not support concurrency

我发现的一种替代方案是这样的:

Task.init {
  self.myResult = await someAsyncFunction()
}
Run Code Online (Sandbox Code Playgroud)

其中myResult必须归属为@State成员变量。

然而,这并没有按照我想要的方式工作,因为不能保证在Task.init()完成并进入下一个语句之前完成该任务。那么我怎样才能同步等待该任务完成呢?

swift swift-concurrency

47
推荐指数
3
解决办法
4万
查看次数

Swift,演员:演员隔离的属性“扫描”不能从非隔离的上下文中改变

我有一个演员:

actor StatesActor {

    var job1sActive:Bool = false
    ...

}
Run Code Online (Sandbox Code Playgroud)

我有一个使用该演员的对象:

class MyObj {
    
    let myStates = StatesActor()
    
    func job1() async {
    
        myStates.job1IsActive = true

    }
}
Run Code Online (Sandbox Code Playgroud)

线:

myStates.job1IsActive = true

出现以下错误:

演员隔离属性“job1IsActive”无法从非隔离上下文中发生突变

如何使用 actor 正确存储/读取状态信息,以便 MyObj 可以使用它来读取和设置状态?

actor swift swift-concurrency

39
推荐指数
2
解决办法
3万
查看次数

“Task”在调用内部异步函数时会阻塞主线程

我有一个ObservableObject类和一个 SwiftUI 视图。当点击按钮时,我会在其中创建Task并调用(异步函数)。populate我以为这会populate在后台线程上执行,但整个 UI 都冻结了。这是我的代码:

class ViewModel: ObservableObject {
    @Published var items = [String]()
    func populate() async {
        var items = [String]()
        for i in 0 ..< 4_000_000 { /// this usually takes a couple seconds
            items.append("\(i)")
        }
        self.items = items
    }
}

struct ContentView: View {
    @StateObject var model = ViewModel()
    @State var rotation = CGFloat(0)

    var body: some View {
        Button {
            Task {
                await model.populate()
            }
        } label: { …
Run Code Online (Sandbox Code Playgroud)

ios async-await swift swiftui swift-concurrency

38
推荐指数
4
解决办法
3万
查看次数

如何防止 Actor 重入导致重复请求?

在 WWDC 2021 视频中,使用 Swift Actor 保护可变状态中,他们提供了以下代码片段:

\n
actor ImageDownloader {\n    private var cache: [URL: Image] = [:]\n\n    func image(from url: URL) async throws -> Image? {\n        if let cached = cache[url] {\n            return cached\n        }\n\n        let image = try await downloadImage(from: url)\n\n        cache[url] = cache[url, default: image]\n\n        return cache[url]\n    }\n\n    func downloadImage(from url: URL) async throws -> Image { ... }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

问题是 actor 提供可重入性,因此cache[url, default: image]引用有效地确保即使您由于某些竞争而执行了重复的请求,您至少在继续之后检查 actor\xe2\x80\x99s 缓存,确保您获得相同的图像对于重复的请求。

\n

在那段视频中,他们

\n
\n …

async-await swift swift-concurrency

34
推荐指数
3
解决办法
2604
查看次数

async/await、Task 和 [weak self]

好的,我们都知道,在 Swift 的传统并发中,如果您在类中执行(例如)网络请求,并且在完成该请求时您引用了属于该类的函数,则必须传入[weak self],例如这:

func performRequest() {
   apiClient.performRequest { [weak self] result in
      self?.handleResult(result)
   }
}
Run Code Online (Sandbox Code Playgroud)

这是为了阻止我们self在闭包中强烈捕获并导致不必要的保留/无意中引用已经从内存中删除的其他实体。

在异步/等待中怎么样?我在网上看到了相互矛盾的事情,所以我只想向社区发布两个示例,看看您对这两个示例有何看法:

class AsyncClass {
   func function1() async {
      let result = await performNetworkRequestAsync()
      self.printSomething()
   }

   func function2() {
      Task { [weak self] in
         let result = await performNetworkRequestAsync()
         self?.printSomething()         
      }
   }

   func function3() {
      apiClient.performRequest { [weak self] result in
         self?.printSomething()
      }
   }

   func printSomething() {
      print("Something")
   }
}

Run Code Online (Sandbox Code Playgroud)

function3很简单 - 老式并发意味着使用[weak self]. function2我认为是对的,因为我们仍在闭包中捕获内容,所以我们应该使用[weak …

concurrency ios async-await swift swift-concurrency

33
推荐指数
2
解决办法
1万
查看次数

在同步非隔离上下文中调用主参与者隔离实例方法 XXX

我正在尝试向 UIButton 添加异步 do/catch/defer 操作。但是,如果我只是调用 defer 块中的方法,则会收到Call to main actor-isolated instance method XXX in a synchronous nonisolated context错误。我发现的解决方法是将其包装在另一个Task块中,如下所示。只是想检查这是否是正确的方法?如果有人能解释该错误消息的实际含义,那就太好了。

@objc private func post(_ sender: UIButton) {
    Task {
        // defer { dismiss(animated: true) } -- Doesn't work
        defer { Task { await dismiss(animated: true) } }
        do {
            try await doSomethingAsync()
        } catch {
            print(error)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

async-await swift swift-concurrency

31
推荐指数
1
解决办法
3万
查看次数

如何使用异步等待 Swift 5.5 等待 x 秒

如何使用新的 Swift 5.5await关键字来等待一段时间?

DispatchQueue通常,对于完成处理程序,您可以通过使用's得到类似的结果asyncAfter(deadline:execute:)

func someLongTask(completion: @escaping (Int) -> Void) {
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        completion(Int.random(in: 1 ... 6))
    }
}

someLongTask { diceRoll in
    print(diceRoll)
}
Run Code Online (Sandbox Code Playgroud)

在 Swift 5.5 中如何将其转换为使用async& ?await

concurrency swift swift5 swift-concurrency

28
推荐指数
2
解决办法
2万
查看次数

如何访问与参与者隔离的属性

我试图检查演员的行为。这是Swift5.5提供的新功能。

我使用官方文档swift.org中的示例代码创建了一个游乐场:

    import Foundation
    
    actor TemperatureLogger {
        let label: String
        var measurements: [Int]
        private(set) var max: Int

        init(label: String, measurement: Int) {
            self.label = label
            self.measurements = [measurement]
            self.max = measurement
        }
    }

    let logger = TemperatureLogger(label: "Outdoors", measurement: 25)
    print(await logger.max)
    // Prints "25"

Run Code Online (Sandbox Code Playgroud)

但我的编译器在这个例子中失败了:

错误

斯威夫特编译器错误:

不支持并发的函数中的“await”

Actor 隔离属性“max”只能从 Actor 内部引用

那么如何访问与参与者隔离的属性呢?

也许这是编译器或示例代码中的错误?

Xcode 版本 13.0 测试版 (13A5154h) Swift 版本 5.5

swift swift5 xcode13 swift-concurrency

24
推荐指数
1
解决办法
3万
查看次数

如何使用 Swift Concurrency 约束并发(如 maxConcurrentOperationCount)?

我正在尝试执行一系列网络请求,并希望限制新的 Swift 并发系统中的并发任务数量。对于操作队列,我们​​将使用maxConcurrentOperationCount. 在组合中,flatMap(maxPublishers:_:). 新的 Swift Concurrency 系统中的等价物是什么?

例如,它并不是非常相关,但请考虑:

func downloadAll() async throws {
    try await withThrowingTaskGroup(of: Void.self) { group in
        for index in 0..<20 {
            group.addTask { try await self.download(index) }
        }

        try await group.waitForAll()
    }
}
Run Code Online (Sandbox Code Playgroud)

这会导致所有请求同时运行:

在此输入图像描述

URLSession不尊重这一事实httpMaximumConnectionsPerHost很有趣,但这不是这里的突出问题。更一般地说,我正在寻找如何限制一系列并行运行的异步任务的并发程度。

async-await swift swift-concurrency

23
推荐指数
1
解决办法
2889
查看次数

如何使用 @MainActor 初始化全局变量?

我想要某种使用 同步的全局变量@MainActor

这是一个示例结构:

@MainActor
struct Foo {}
Run Code Online (Sandbox Code Playgroud)

我想要一个像这样的全局变量:

let foo = Foo()
Run Code Online (Sandbox Code Playgroud)

但是,这不会编译并出现错误Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context

很公平。我尝试在主线程上构建它,如下所示:

let foo = Foo()
Run Code Online (Sandbox Code Playgroud)

这可以编译!但是,它会崩溃EXC_BAD_INSTRUCTION,因为DispatchQueue.main.sync无法在主线程上运行。

我还尝试创建一个包装函数,例如:

let foo = DispatchQueue.main.sync {
    Foo()
}
Run Code Online (Sandbox Code Playgroud)

并使用

let foo = syncMain {
    Foo()
}
Run Code Online (Sandbox Code Playgroud)

但编译器无法识别if Thread.isMainThread并再次抛出相同的错误消息Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context

这样做的正确方法是什么?我需要某种可以在应用程序启动之前初始化的全局变量。

swift swift-concurrency

20
推荐指数
1
解决办法
1万
查看次数