提问人:damdamo 提问时间:10/12/2023 更新时间:10/12/2023 访问量:54
Swift 函数超时
Timeout for Swift functions
问:
我写完了一个 Swift 库,我需要在一些示例上测试它的性能。
我正在 Swift 中创建一个脚本,将一些数据放入文件中,使用我的库执行它们,然后返回执行时间等结果。
在从这些文件中提取的这些不同示例的列表中,其中一些可能需要数小时才能执行,而另一些则只需要几秒钟。我不期望所有示例都有答案,我只想要那些在不到“x”秒内完成的结果。出于这个原因,如果函数的调用时间超过“x”秒,我想引入超时。它应该停止当前执行并转到下一个执行。
我寻找解决方案,其中一些使用 async/await 或 dispatchQueue,但没有一个对我有用。以下是我尝试过的解决方案之一:
func evalWithTimer(key: String, somethingToExecute: SomethingToExecute) -> SVS? {
var res: SVS? = nil
// Create a semaphore
let semaphore = DispatchSemaphore(value: 0)
// Define the time limit in seconds
let timeLimit: DispatchTime = .now() + 1 // 1 seconds
// Define the task you want to run (replace this with your actual task)
let task = {
res = somethingToExecute.eval()
semaphore.signal() // Signal that the task is done
}
// Execute the task asynchronously
DispatchQueue.global().async(execute: task)
// Wait for the task to complete or time out
let result = semaphore.wait(timeout: timeLimit)
if result == .timedOut {
// Task did not complete within the time limit, you can cancel it if needed
print("Task evaluation timed out and was canceled")
return res
} else {
// Task completed within the time limit
print("Task evaluation completed within the time limit")
return res
}
}
我的函数 evalWithTimer 是针对 somethingToExecute 中的不同值调用的。当我调用它时,它可能需要 1 秒以上,如果这是真的,它应该停止。在此当前版本中,它不起作用,因为未正确取消任务。至少这是我的假设。即使在超时为 true 时添加也不会更改任何内容。eval()
eval()
task.cancel()
我发现的唯一解决方案添加了异步机制,这是可以理解的。我正在寻找这个问题的解决方案,无论是否基于上面的代码。
请注意,这不是异步的。somethingToExecute.eval()
答:
如果您正在进行“测试”,我建议使用 XCTest。您可以等待超时。XCTestExpectation
func testExample() {
let expect = expectation(description: "Eval completed in 1 second")
DispatchQueue.global().async {
somethingToExecute.eval()
expect.fulfill()
}
wait(for: [expect], timeout: 1)
}
当超过超时时,测试进程将被终止。
不过,总的来说,停止事情应该是一种合作努力。 应该检查自己是否被取消,如果是,停止它正在做的任何事情。eval
例如,使用 Swift Concurrency,可以在其执行过程中的适当时间点检查 Task.isCancelled
,或者如果 ,则可以。eval
throws
try Task.checkCancellation()
假设是一个长时间运行的循环,你可以在每次迭代中进行检查:eval
while something {
if Task.isCancelled { return nil /* or however you'd like to stop it*/ }
// or in a throwing function:
// try Task.checkCancellation()
// do work
}
然后,您可以执行两个任务,一个是 运行 ,另一个测量 1 秒,然后取消第一个任务。eval
func evalWithTimer(key: String, somethingToExecute: SomethingToExecute) async throws -> SVS? {
let evalTask = Task {
somethingToExecute.eval()
// if eval is throwing,
// try somethingToExecute.eval()
}
let timeoutTask = Task {
try await Task.sleep(for: .seconds(1))
evalTask.cancel()
// here you know the timeout is exceeded
}
// if eval is throwing, "try" await, a CancellationError would be thrown if the timeout exceeds
let result = await evalTask.value
timeoutTask.cancel()
return result
}
评论
eval
eval()
XCTestExpectation