我遇到了一个类实例没有被释放的问题,所以应用程序卡住了。导致问题的课程是
import Foundation
class ResponseTimeoutQueue{
private let mMsgOut: [UInt8]
private let mResponseMsgId: Int
private let mTimeoutMilliSec: UInt32
private let mNumRetries: Int
private var timeoutWorkItem: DispatchWorkItem?
init(responseMsgId: Int, msgOut: [UInt8], timeoutMilliSec: UInt32, numRetries: Int) {
mResponseMsgId = responseMsgId
mMsgOut = msgOut
mTimeoutMilliSec = timeoutMilliSec
mNumRetries = numRetries
}
func run() {
// create a work item with the custom code
timeoutWorkItem = DispatchWorkItem {
// Insert your code here
var retryNum: Int = 0
var doRetry: Bool = true
while (doRetry) {
Thread.sleep(forTimeInterval: TimeInterval(self.mTimeoutMilliSec))
// If we are here then it means the last send did not receive a response before
// timing out. Write with no timeout or num retries so we don't spawn another
// ResponseTimeoutQueue.
retryNum += 1
if (retryNum <= self.mNumRetries) {
SessionController.sharedController.invokeWriteData(responseMsgId: self.mResponseMsgId, bytes: self.mMsgOut)
} else {
doRetry = false
}
}
// Notify the handler.
NotificationCenter.default.post(name: .timeOutMessage, object: -1)
}
//Create dispatch group
let dispatchGroup = DispatchGroup()
// execute the workItem with dispatchGroup
DispatchQueue.global().async(group: dispatchGroup, execute: timeoutWorkItem!)
//Handle code after the completion of global queue
dispatchGroup.notify(queue: DispatchQueue.global()) {
}
timeoutWorkItem?.cancel()
}
func interrupt() {
timeoutWorkItem?.cancel()
timeoutWorkItem = nil
}
}
run()函数的调用发生在
func writeNotify(responseMsgId: Int, buffer: [UInt8], timeoutMilliSec: UInt32, numRetries: Int) {
if (timeoutMilliSec > 0) {
mResponseMonitorThreadMap[responseMsgId]?.interrupt()
let timeoutThread: ResponseTimeoutQueue =
ResponseTimeoutQueue(responseMsgId: responseMsgId, msgOut: buffer,
timeoutMilliSec: timeoutMilliSec, numRetries: numRetries)
timeoutThread.run()
mResponseMonitorThreadMap[responseMsgId] = timeoutThread
}
}
基本上我们从雷达请求数据,当我们得到数据时我们调用
func cancelResponseTimeout(responseMsgId: Int) {
mResponseMonitorThreadMap[responseMsgId]?.interrupt()
if mResponseMonitorThreadMap.keys.contains(responseMsgId){
mResponseMonitorThreadMap[responseMsgId] = nil
}
}
但在与雷达通信几分钟后,responseTimeoutQueue 线程数为 65 或更多。我注释了writeToNotify()函数,那么应用就没有问题了。我将附上内存层次结构屏幕截图。我尝试在调用 timeoutThread.run() 后给出
timeoutThread = nil
,但类实例编号没有改变。如何解决这个问题?提前致谢。
我可以在这里看到许多问题。第一个也是最重要的是在这一行:
Thread.sleep(forTimeInterval: TimeInterval(self.mTimeoutMilliSec))
A
TimeInterval
是 Double
的别名,它代表 seconds 的数量。如果你想睡 500 毫秒,你需要 TimeInterval(500)/1000
或 TimeInterval(0.5)
.
我也不确定所有对
cancel()
的调用的意义是什么。 run()
中的那个是竞争条件,因为它可能在工作项开始执行之前被调用。另一个什么都不做,因为 cancel()
只会取消尚未开始执行的项目。
你确实有一个强大的参考循环,但我认为如果你在调度组完成处理程序中将
timeoutWorkItem
设置为 nil
会被破坏。
说到调度组:你只有一个项目,所以你真的不需要它。您可以将完成处理程序直接放在工作项上,并在那里将属性设置为
nil
。
编辑
OK,再看一眼。最大的问题不是超级长时间的超时,而是
cancel()
没有按照你的想法去做。这是文档:
取消会导致未来执行工作项的尝试立即返回。 取消不影响已经开始的工作项的执行.
[我的斜体]
我认为你最好设置一个
NSTimer
或 DispatchSourceTimer
超时,如果你得到响应则取消它,然后它的事件块可以重试。