英文:
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
URLSession
是 Sendable
,并且可以安全地跨并发域传递。文档 也明确告诉我们它是线程安全的:
> ### 线程安全
>
> 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论