Dan*_*iel 6 cancellation async-await swift
我需要支持取消一个函数,该函数返回一个可以在启动后取消的对象。就我而言,该类requester位于我无法修改的第三方库中。
actor MyActor {
...
func doSomething() async throws -> ResultData {
var requestHandle: Handle?
return try await withTaskCancellationHandler {
requestHandle?.cancel() // COMPILE ERROR: "Reference to captured var 'requestHandle' in concurrently-executing code"
} operation: {
return try await withCheckedThrowingContinuation{ continuation in
requestHandle = requester.start() { result, error in
if let error = error
continuation.resume(throwing: error)
} else {
let myResultData = ResultData(result)
continuation.resume(returning: myResultData)
}
}
}
}
}
...
}
Run Code Online (Sandbox Code Playgroud)
我已经审查了其他问题和此线程:https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341/4
有些案例非常相似,但又不完全相同。由于以下错误,此代码将无法编译:
“在并发执行的代码中引用捕获的 var 'requestHandle'”
我假设编译器试图在requestHandle初始化之前保护我不使用它。但我不确定还有什么办法可以解决这个问题。Swift 论坛讨论线程中显示的其他示例似乎都有一种模式,requester可以在调用其函数之前初始化对象start。
我还尝试将其保存requestHandle为类变量,但在同一位置出现了不同的编译错误:
无法从 Sendable 闭包引用 Actor 隔离属性“profileHandle”
你说:
\n\n\n我假设编译器试图保护我不使用
\nrequestHandle它初始化之前的\xe2\x80\x99s。
或者,更准确地说,它只是保护您免受竞争。您需要同步与您的 \xe2\x80\x9crequester\xe2\x80\x9d 的交互以及Handle.
\n\n但我\xe2\x80\x99m 不知道如何解决这个问题。Swift 论坛讨论线程中显示的其他示例似乎都有一种模式,
\nrequester可以在调用其函数之前初始化对象start。
是的,这正是你应该做的。不幸的是,您没有\xe2\x80\x99t 分享您的requester初始化位置或实现方式,因此我们很难对您的具体情况发表评论。
但根本问题是您需要同步您的start和cancel. 因此,如果您requester尚未执行此操作,则应将其包装在提供线程安全交互的对象中。在 Swift 并发中执行此操作的标准方法是使用参与者。
例如,让我们假设您正在包装一个网络请求。要与此同步您的访问,您可以创建一个参与者:
\nactor ResponseDataRequest {\n private var handle: Handle?\n\n func start(completion: @Sendable @escaping (Data?, Error?) -> Void) {\n // start it and save handle for cancelation, e.g.,\n \n handle = requestor.start(...)\n }\n\n func cancel() {\n handle?.cancel()\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这将网络请求的启动和取消包装在参与者中。然后你可以做这样的事情:
\nfunc doSomething() async throws -> ResultData {\n let responseDataRequest = ResponseDataRequest()\n\n return try await withTaskCancellationHandler {\n Task { await responseDataRequest.cancel() }\n } operation: {\n return try await withCheckedThrowingContinuation { continuation in\n Task {\n await responseDataRequest.start { result, error in\n if let error = error {\n continuation.resume(throwing: error)\n } else {\n let resultData = ResultData(result)\n continuation.resume(returning: resultData)\n }\n }\n }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n当您验证所有内容都与您检查的延续一起工作时,您显然可以转向不安全的延续。
\n