标签: swift-concurrency

为什么 swift 不警告这个不可发送的全局传递到不同的任务?

考虑以下代码:

class Cat {
    var name = "Tom"
}

class Globals {
    var cat = Cat()
}

let glob = Globals()

func one () {
    Task {glob.cat.name="Max"} // Expected Warning about some nonSendable moving into a different concurrency domain
}
Run Code Online (Sandbox Code Playgroud)

通常,-warn-concurrency启用后,Swift/Xcode 会警告不可发送的内容跨并发域。

根据我的理解,传递给的闭包Task必须始终是一个@Sendable闭包。闭包@Sendable只能捕获 Sendables。但是,Globals 不是可发送类型。我预计会出现类似以下的警告

@Sendable在闭包中捕获具有不可发送类型“Globals”的“glob”

不会发出此类警告。

swift swift-concurrency

6
推荐指数
0
解决办法
292
查看次数

异步自动释放池

我有一种情况,我正在循环中创建许多 Cocoa 对象async/await,并且内存峰值,因为这些对象仅在循环结束时释放(而不是每次迭代)。

解决方案是使用autoreleasepool. 但是,我似乎无法autoreleasepoolasync/await

这是一个例子:

func getImage() async -> NSImage? {
    return NSImage(named: "imagename") // Do some work
}

Task {
    // This leaks
    for _ in 0 ..< 1000000 {
        let image = await getImage()
        print(image!.backgroundColor)
    }
}
Run Code Online (Sandbox Code Playgroud)

内存一路飙升至 220MB,这对我来说有点太多了。

通常,您可以将内部循环包装在 a 中autoreleasepool,它可以解决问题,但是当我使用函数尝试它时async,我收到此错误:

Cannot pass function of type '() async -> ()' to parameter expecting synchronous function type
Run Code Online (Sandbox Code Playgroud)

有没有办法解决?或者是否有另一种方法可以实现在循环内释放 Cocoa 对象的相同目标?

macos cocoa async-await swift swift-concurrency

5
推荐指数
1
解决办法
1263
查看次数

Swift 任务没有按预期在主要参与者上运行

我在一个简单的 Mac 测试项目中有以下代码:

\n
@main\nclass AppDelegate: NSObject, NSApplicationDelegate {\n  func applicationDidFinishLaunching(_ aNotification: Notification) {\n    print("are we initially on the main thread: \\(Thread.isMainThread)")\n\n    Task {\n      print("is new task on main thread: \\(Thread.isMainThread)")\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Swift 语言指南中,我看到以下内容:

\n
\n

要创建在当前参与者上运行的非结构化任务,请调用 Task.init(priority:operation:) 初始值设定项。要创建不属于当前参与者的非结构化任务(更具体地称为分离任务),请调用 Task.detached(priority:operation:) 类方法。

\n
\n

因此,由于 AppDelegate 代码在主要参与者上运行,并且我正在创建一个正常(即非分离)任务,因此我希望它的子任务也在主要参与者上运行。

\n

但当我运行这个测试应用程序时,情况并非如此:

\n
are we initially on the main thread: true\nis new task on main thread: false\n
Run Code Online (Sandbox Code Playgroud)\n

根据我所读到的有关 Swift 并发性的内容,我预计新任务将在主要参与者上调度并运行,因此Thread.isMainThread在该任务中也是如此。

\n

为什么事实并非如此?

\n

swift swift-concurrency

5
推荐指数
1
解决办法
1356
查看次数

SwiftUI的任务修饰符示例代码令人困惑

这是苹果开发者文档\xe3\x80\x82中的代码

\n
let url = URL(string: "https://example.com")!\n@State private var message = "Loading..."\n\nvar body: some View {\n    Text(message)\n        .task {\n            do {\n                var receivedLines = [String]()\n                for try await line in url.lines {\n                    receivedLines.append(line)\n                    message = "Received \\(receivedLines.count) lines"\n                }\n            } catch {\n                message = "Failed to load"\n            }\n        }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

为什么不在messageUI 线程中更新,如下代码所示

\n
DispatchQueue.main.async {\n    message = "Received \\(receivedLines.count) lines"\n}\n
Run Code Online (Sandbox Code Playgroud)\n

任务块中的代码是否始终在UI线程中运行?

\n

这是我的测试代码。有时任务似乎没有继承其调用者的参与者上下文。

\n
func wait() async {\n    await Task.sleep(1000000000)\n}\n\nThread.current.name = "Main thread"\nprint("Thread in top-level is \\(Thread.current.name)")\n\nTask …
Run Code Online (Sandbox Code Playgroud)

swiftui swift-concurrency

5
推荐指数
1
解决办法
1153
查看次数

从无任务上下文同步访问参与者属性

与现有代码集成actors似乎并不像苹果希望您相信的那么简单。考虑以下简单的参与者:

actor Foo {
    var value: Int = 0
}
Run Code Online (Sandbox Code Playgroud)

AppKit/UIKit尝试从任何(无任务)控制器访问此属性都是行不通的,因为每个Task控制器都是异步的。

class AppKitController {

    func myTaskLessFunc() {
        let f = Foo()
        var v: Int = -1
        Task { v = await f.value }
    }
}
Run Code Online (Sandbox Code Playgroud)

这会给你带来预期的错误Mutation of captured var 'v' in concurrently-executing code,而且如果你试图锁定它,它也不会工作。nonisolated演员方法也没有帮助,因为你会遇到同样的问题。

那么,如何在无任务上下文中同步读取参与者属性呢?

swift swift-concurrency

5
推荐指数
1
解决办法
2736
查看次数

MainActor 与 MainActor(不安全)

我们可以创建一个@MainActor Task这样的:

Task { @MainActor in
    print("hi")
}
Run Code Online (Sandbox Code Playgroud)

但也有@MainActor(unsafe)这样的:

Task { @MainActor(unsafe) in
    print("hi")
}
Run Code Online (Sandbox Code Playgroud)

这两种方法有什么区别?

swift swift-concurrency

5
推荐指数
1
解决办法
859
查看次数

Swift Concurrency AsyncStream 中的类型擦除

在试验 Swift 的并发性时,我希望有一个干净的 API 来公开给定元素类型的异步序列及其节流版本:

  var intStream: AsyncStream<Int> {
    AsyncStream<Int>(Int.self, bufferingPolicy: .bufferingNewest(5)) { continuation in
      Task.detached {
        for _ in 0..<100 {
          try await Task.sleep(nanoseconds: 1 * 1_000_000_000)
          continuation.yield(Int.random(in: 1...10))
        }
        continuation.finish()
      }
    }
  }
  
  var throttledIntStream: AsyncStream<Int> {
    intStream.throttle(for: .seconds(2))
  }
Run Code Online (Sandbox Code Playgroud)

但这不起作用,因为节流阀返回其自己的类型:错误: Cannot convert return expression of type 'AsyncThrottleSequence<AsyncStream<Int>, ContinuousClock, Int>' to return type 'AsyncStream<Int>'

为了获得类型擦除我可以做

var throttledIntStream: some AsyncSequence {
  intStream.debounce(for: Duration.seconds(2))
}
Run Code Online (Sandbox Code Playgroud)

但随后我也丢失了我想保留的元素类型信息。

有什么建议如何最好地解决这个问题?

编辑:这指向我想要的解决方案,但我想我需要等待https://forums.swift.org/t/anyasyncsequence/50828/2

swift swift-concurrency

5
推荐指数
1
解决办法
928
查看次数

在调用声明为 @MainActor 的异步函数的任务中使用 @MainActor

我想了解是否需要考虑以下代码块将任务本身声明为 MainActor。

func login() {
    Task { [weak self] in
        let result = await self?.loginService.start()

        if result == .successful {
            self?.showWelcomeMessage() // updating the UI here
        }
    }
}

final class LoginService {

    @MainActor
    func start() async -> LoginResult {
        // Doing some UI related operations here
    }
}

Run Code Online (Sandbox Code Playgroud)

当任务内部调用的异步函数已经声明为 MainActor 时,我是否还需要将任务本身声明为 MainActor ?像这样:

func login() {
    Task { @MainActor [weak self] in
        let result = await self?.loginService.start()

        if result == .successful {
            self?.showWelcomeMessage() // updating the UI here
        } …
Run Code Online (Sandbox Code Playgroud)

ios async-await swift mainactor swift-concurrency

5
推荐指数
1
解决办法
1886
查看次数

MainActor 隔离的可变存储属性为何会出现可发送错误?

我正在尝试使一个类符合Sendable. 我有一些可变的存储属性导致了问题。但是,我无法理解的是 MainActor 独立属性不允许我的类符合 Sendable。但是,如果我将整个班级标记为 a @MainActor,那就没问题了。然而,我实际上并不想让全班都遵守@MainActor

举个例子,看下面的代码:

final class Article: Sendable {
  @MainActor var text: String = "test"
}
Run Code Online (Sandbox Code Playgroud)

它给出了这样的警告:Stored property 'text' of 'Sendable'-conforming class 'Article' is mutable

有人可以解释为什么吗?我认为与演员隔离会很好。

swift swift-concurrency

5
推荐指数
1
解决办法
1403
查看次数

为什么 Swift 不在启动的同一个线程上恢复异步函数?

在《Swift 编程语言》并发章节的介绍部分中, 我读到:

\n
\n

当异步函数恢复时,Swift 不会对该函数将在哪个线程上运行做出任何保证。

\n
\n

这让我很惊讶。与等待 pthread 中的信号量相比,执行可以跳转线程,这似乎很奇怪。

\n

这让我产生以下问题:

\n
    \n
  • 为什么 Swift 不保证在同一个线程上恢复?

    \n
  • \n
  • 是否有任何规则可以\n确定恢复线程?

    \n
  • \n
  • 有没有办法影响这种行为,例如确保它在主线程上恢复?

    \n
  • \n
\n

编辑:我对 Swift 并发性和上述后续问题的研究是由于发现在主线程Task(在 SwiftUI 中)上运行的代码正在另一个线程上执行它的块而触发的。

\n

asynchronous ios swift swift-concurrency

5
推荐指数
2
解决办法
821
查看次数