具有共享状态的演员

huangapple go评论48阅读模式
英文:

Actors with shared state

问题

以下是翻译好的部分:

"The following two actors share the same URLSession instance. The actors work in isolation, so is this a problem because each actor may change properties on the session at the same time? How can I protect against this?"

"以下的两个角色共享相同的URLSession实例。这些角色在隔离中工作,因此因为每个角色可能同时更改会话的属性,这会成为一个问题吗?如何保护自己免受此影响?"

actor ImageDownloaderA {
    
    let session = URLSession.shared
    
    func fetchImage() async throws -> Data {
        let request = ...
        return try await session.data(for: ...)
    }
}

actor ImageDownloaderB {
    
    let session = URLSession.shared
    
    func fetchImage() async throws -> Data {
        let request = ...
        return try await session.data(for: ...)
    }
}

struct Fetcher {
    
    func fetcher() async throws {
        let downloaderA = ImageDownloaderA()
        let downloaderB = ImageDownloaderB()
        _ = try await downloaderA.fetchImage()
        _ = try await downloaderB.fetchImage()
    }
}
英文:

The following two actors share the same URLSession instance. The actors work in isolation, so is this a problem because each actor may change properties on the session at the same time? How can I protect against this?

actor ImageDownloaderA {
    
    let session = URLSession.shared
    
    func fetchImage() async throws -> Data {
        let request = ...
        return try await session.data(for: ...)
    }
}

actor ImageDownloaderB {
    
    let session = URLSession.shared
    
    func fetchImage() async throws -> Data {
        let request = ...
        return try await session.data(for: ...)
    }
}

struct Fetcher {
    
    func fetcher() async throws {
        let downloaderA = ImageDownloaderA()
        let downloaderB = ImageDownloaderB()
        _ = try await downloaderA.fetchImage()
        _ = try await downloaderB.fetchImage()
    }
}

答案1

得分: 2

URLSessionSendable,并且可以安全地跨并发域传递。文档 也明确告诉我们它是线程安全的:

> ### 线程安全
>
> URL 会话 API 是线程安全的。您可以在任何线程上自由创建会话和任务。当您的委托方法调用提供的完成处理程序时,工作会自动安排到正确的委托队列上。


需要注意的是,这两个请求实际上并不是同时执行的。这并不是因为 URLSession,而是因为这段代码会在开始 ImageDownloaderB.fetchImage 之前 await 完成 ImageDownloaderA.fetchImage。如果我们想要并发执行,可以使用 async let 或任务组。

现在,您没有向我们展示您对这两个响应正在执行的操作,但让我们想象,出于演示目的,您希望它们只是打印每个返回的字节数。

func fetcher() async throws {
    let downloaderA = ImageDownloaderA()
    let downloaderB = ImageDownloaderB()

    async let data1 = downloaderA.fetchImage()
    async let data2 = downloaderB.fetchImage()
    
    let count1 = try await data1.count
    let count2 = try await data2.count
    
    print(count1, count2)
}

这将同时运行这些请求,因为我们使用了 async let,并且仅在两者都启动后才引入 await 暂停点。有关 async let 的详细信息,请参见 SE-0317


但无论如何,您可以在不同的并发上下文中使用相同的 URLSession,而不会发生问题。

英文:

URLSession is Sendable and is safe to be passed across concurrency domains. The documentation also explicitly tells us that it is thread-safe:

> ### Thread Safety
>
> The URL session API is thread-safe. You can freely create sessions and
> tasks in any thread context. When your delegate methods call the
> provided completion handlers, the work is automatically scheduled on
> the correct delegate queue.


It should be noted that these two requests are not actually executing concurrently. This is not because of URLSession, but rather because this code will await the completion of ImageDownloaderA.fetchImage before even starting ImageDownloaderB.fetchImage. If we want concurrent execution, we might use async let or a task group.

Now, you’re not showing us what you’re doing with these two responses, but let’s imagine that for demonstration purposes you wanted them to simply print the number of bytes that each returned.

func fetcher() async throws {
    let downloaderA = ImageDownloaderA()
    let downloaderB = ImageDownloaderB()

    async let data1 = downloaderA.fetchImage()
    async let data2 = downloaderB.fetchImage()
    
    let count1 = try await data1.count
    let count2 = try await data2.count
    
    print(count1, count2)
}

That will run the requests concurrently because we used async let and then only introduced await suspension points after both had been started. For details on async let, see SE-0317.


But, regardless, you can use the same URLSession in separate concurrency contexts without incident.

huangapple
  • 本文由 发表于 2023年2月16日 13:12:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/75468083.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定