我完成了一个 Swift 库的编写,我需要在一些示例上测试它的性能。
我正在 Swift 中创建一个脚本,将一些数据放入文件中,使用我的库执行它们,然后返回执行时间等结果。
从这些文件中提取的不同示例列表中,其中一些可能需要数小时才能执行,而另一些则只需几秒钟。我不希望得到所有示例的答案,我只想要那些在“x”秒内完成的示例的结果。因此,如果我的函数调用时间超过“x”秒,我想引入超时。它应该停止当前执行并转到下一个。
我寻找解决方案,其中一些使用异步/等待或调度队列,但没有任何对我有用。以下是我尝试过的解决方案之一:
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 中的不同值。当我对其调用
eval()
时,可能会花费超过 1 秒的时间,如果这是真的,则应该停止。在当前版本中,它不起作用,因为任务 eval()
未正确取消。至少这是我的假设。即使在超时为真时添加 task.cancel()
也不会改变任何东西。
我发现的唯一解决方案添加了异步机制,这是可以理解的。我正在寻找这个问题的解决方案,无论是否基于上面的代码。
请注意,
somethingToExecute.eval()
不是异步的。
阻止事情发生应该是合作的努力。
eval
应检查自身是否被取消,如果是,则停止正在执行的任何操作。
例如,使用 Swift Concurrency,
eval
可以在执行过程中的适当位置检查 Task.isCancelled
,或者如果是 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
}
然后,您可以执行两个任务,一个运行
eval
,另一个测量 1 秒,然后取消第一个任务。
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
}