Swift代码在不应运行时运行两次
我有一个转义函数,它在满足条件后完成:Swift代码在不应运行时运行两次,swift,asynchronous,dispatchgroup,Swift,Asynchronous,Dispatchgroup,我有一个转义函数,它在满足条件后完成: private func xxxfastLoadLSecurityDescriptions(session: URLSession, mySymbols: [String]?, completion: @escaping(Bool) ->()) { var counter = mySymbols?.count ?? 0 if counter == 0 { completion(false)
private func xxxfastLoadLSecurityDescriptions(session: URLSession, mySymbols: [String]?, completion: @escaping(Bool) ->()) {
var counter = mySymbols?.count ?? 0
if counter == 0 { completion(false) }
var doubleCount = 0
// print("DESCRIPTION Starting Counter = \(counter)")
for symbolIndex in 0..<(mySymbols?.count ?? 0) {
guard let mySymbol = mySymbols?[symbolIndex] else { print("ERROR in fastLoadLSecurityDescriptions loop: No Symbol") ; continue }
guard let myGetDescriptionRequest = GenericDataRequest(dataToSend: [mySymbol], token: sessionToken, mgbRoute: MGBServerRoutes.retrieveSecurityDescriptionsRoute!)
else { print("Error Getting Security Description Request for \(mySymbol)") ; return }
mySessionSendMGBGenericRequest(session: session, request: myGetDescriptionRequest) { [weak self] success, serverMessage, returnUNISecurityDescription in
guard let self = self else { print("ERROR: self is nil") ; return }
if returnUNISecurityDescription?.count == 0 { print("nil returnUniSecurityDescription for \(mySymbol)") }
// print("DESCRIPTIONS COUNTER = \(counter)")
counter -= 1
var myDescription = UNISecurityDescription()
if returnUNISecurityDescription != nil, returnUNISecurityDescription?.count != 0 { myDescription = returnUNISecurityDescription![0] }
if myDescription.name == nil || myDescription.name == "" { print("Error: No Name for \(String(describing: mySymbol))") }
let myContainersIndices = self.myUNIList.singleContainer.indices.filter({ self.myUNIList.singleContainer[$0].ticker?.symbol == mySymbol })
var myPathArray = [IndexPath]()
for index in 0..<myContainersIndices.count {
self.myUNIList.singleContainer[myContainersIndices[index]].name = myDescription.name
self.myUNIList.singleContainer[myContainersIndices[index]].currencySymbol = myDescription.currencySymbol
self.myUNIList.singleContainer[myContainersIndices[index]].fillFundamentals() // --> Fills the outputs for sortdata
myPathArray.append(IndexPath(row: myContainersIndices[index], section: 0))
}
DispatchQueue.main.async {
self.filteredData = self.myUNIList.singleContainer
self.myCollection?.reloadItems(at: myPathArray)
}
if counter == 0 { // THIS IS TRUE MORE THAN ONCE WHILE IT SHOULD NOT BE TRU MORE THAN ONCE
if doubleCount > 0 { print("WHY!!!!") }
doubleCount += 1
print("DESCRIPTIONS counter = \(counter) -> \(self.myUNIList.listName) - symbols: \(String(describing: mySymbols?.count)) \n==================================================\n")
DispatchQueue.main.async { self.sortNormalTap("Load") { _ in self.displayAfterLoading() } }
completion(true)
return
}
}
}
}
private func xxxfastLoadLSecurityDescriptions(会话:URLSession,mySymbols:[String]?,完成:@escaping(Bool)->()){
变量计数器=mySymbols?.count±0
如果计数器==0{完成(假)}
var doubleCount=0
//打印(“说明开始计数器=\(计数器)”)
对于0..0{print(“WHY!!!!”)}中的symbolIndex
双重计数+=1
打印(“描述计数器=\(计数器)->\(self.myUNIList.listName)-符号:\(字符串(描述:mySymbols?.count))\n===================================================================================================\n”)
DispatchQueue.main.async{self.sortNormalTap(“加载”){{in self.displayAfterLoading()}
完成(真)
返回
}
}
}
}
要满足的条件是计数器==0。满足此条件后,函数完成并退出DispatchGroup。问题是计数器==0多次为真(退出DispatchGroup时出现明显的崩溃)。我真的不明白为什么这个条件被满足了不止一次。代码是非常线性的,我看不出是什么导致了这一点。非常感谢您的帮助。这让我抓狂。你的代码不是线程安全的,尤其是计数器。我用同样的逻辑写了一个例子来说明这一点。如果运行几次,您最终将遇到与问题发生时相同的情况
override func viewDidLoad() {
super.viewDidLoad()
let mySymbols: [Int] = Array(0...100)
for _ in 0..<100 {
xxxfastLoadLSecurityDescriptions(session: URLSession.shared, mySymbols: mySymbols) { (success, counter, doubleCount) in
print("Completed: \(success), Counter: \(counter), Double Count: \(doubleCount)")
}
}
}
private func xxxfastLoadLSecurityDescriptions(session: URLSession, mySymbols: [Int]?, completion: @escaping(Bool, Int, Int) ->()) {
var counter = mySymbols?.count ?? 0
if counter == 0 {
return completion(false, -1, -1)
}
var doubleCount = 0
for symbolIndex in 0..<(mySymbols?.count ?? 0) {
guard let _ = mySymbols?[symbolIndex] else {
print("Error")
continue
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(Int.random(in: 50..<900))) {
counter -= 1
DispatchQueue.main.async {
self.view.layoutIfNeeded()
}
if counter == 0 {
if doubleCount > 0 {
// This will eventually print even though logically it shouldn't
print("*****Counter: \(counter), Double Count: \(doubleCount), Symbol Index: \(symbolIndex)")
}
doubleCount += 1
completion(true, counter, doubleCount)
return
}
}
}
}
你的代码不是线程安全的,尤其是计数器。我用同样的逻辑写了一个例子来说明这一点。如果运行几次,您最终将遇到与问题发生时相同的情况
override func viewDidLoad() {
super.viewDidLoad()
let mySymbols: [Int] = Array(0...100)
for _ in 0..<100 {
xxxfastLoadLSecurityDescriptions(session: URLSession.shared, mySymbols: mySymbols) { (success, counter, doubleCount) in
print("Completed: \(success), Counter: \(counter), Double Count: \(doubleCount)")
}
}
}
private func xxxfastLoadLSecurityDescriptions(session: URLSession, mySymbols: [Int]?, completion: @escaping(Bool, Int, Int) ->()) {
var counter = mySymbols?.count ?? 0
if counter == 0 {
return completion(false, -1, -1)
}
var doubleCount = 0
for symbolIndex in 0..<(mySymbols?.count ?? 0) {
guard let _ = mySymbols?[symbolIndex] else {
print("Error")
continue
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(Int.random(in: 50..<900))) {
counter -= 1
DispatchQueue.main.async {
self.view.layoutIfNeeded()
}
if counter == 0 {
if doubleCount > 0 {
// This will eventually print even though logically it shouldn't
print("*****Counter: \(counter), Double Count: \(doubleCount), Symbol Index: \(symbolIndex)")
}
doubleCount += 1
completion(true, counter, doubleCount)
return
}
}
}
}
最后,我使用DispatchGroup解决了这个问题,如下所示(简化代码):
private func xxxFastLoadLSecurityDescriptions(会话:URLSession,mySymbols:[String]?,完成:@escaping(Bool)->()){
让myGroup=DispatchGroup()
对于0..中的symbolIndex,最后我使用DispatchGroup解决了这个问题,如下所示(简化代码):
private func xxxFastLoadLSecurityDescriptions(会话:URLSession,mySymbols:[String]?,完成:@escaping(Bool)->()){
让myGroup=DispatchGroup()
对于0中的symbolIndex,..mySessionSendMGBGenericRequest
不会在主线程上执行,因此当您更改其块中的计数器时,其他线程将不会知道它。您可以使计数器
静态,以便每个块更新相同的值,这不是一种很好的方法,因为实际上应该是递增的某种回调或委托方法中的计数器,但它应该工作。static var counter=0
并使用Self.counter
访问值。可能还应该在async
调用中对其进行反增量。@clawesome:计数器工作正常。它在块中必须是唯一的。它在每个从mySessionSendMGBGenericRequest返回,它会线性地变为0。这部分起作用。不起作用的是if计数器==0的值为true两次(或更多),所以我想知道是什么让执行不止一次。当计数器为0时,似乎在完成块后缺少一个返回。if语句之后的代码仍在执行。我猜这不是您想要的。您是否可能在循环中调用xxxfastLoadLSecurityDescriptions
e> 完成(真)
和之后的返回
,以中断mySymbols?上的for
循环。count
?mySessionSendMGBGenericRequest
不会在主线程上执行,因此当您在其块中更改计数器时,其他线程将不会知道它。您可以使计数器
静态,以便每个块k更新相同的值,这不是一个很好的方法,因为您确实应该在某种回调或委托方法中递增计数器,但它应该可以工作。static var counter=0
并使用Self.counter
访问值。可能还应该在async
调用中取消递增。@clawesome:计数器工作正常。它在块内必须是唯一的。每次从mySessionSendMGBGenericRequest返回时,它都会递减,并且会线性地变为0。这部分工作正常。不工作的是,if计数器==0的值为true两次(或更多),所以我想知道是什么让执行不止一次。当计数器为0时,似乎在完成块后缺少一个返回。if语句之后的代码仍在执行。我猜这不是您想要的。您是否可能在循环中调用xxxfastLoadLSecurityDescriptions
e> 完成(真)
和它后面的return
来打断mySymbols?上的循环。count
?@goerginsn:在完成块之后没有代码。添加return不会改变任何东西。我看到了你的评论。但是我不明白。我看到了很多关于如何计数的示例,当HTTP调用的所有必需响应都失败时op返回的逻辑与我实现的逻辑类似。您将如何执行此操作?块不应负责计数器的反增量,因为它超出了它的作用域。在块反增量计数器的时间和它检查计数器是否为0的时间之间,其他线程之一也可以反增量计数器。您应该有一个调用程序有计数器,并且调用方应该在回调完成时反递增计数器。你能链接你提到的一个例子吗?我想我现在明白了。不确定hot是否能实现线程安全的方法来保持计数。你的回答很有意义(抱歉,但我不是异步的)
private func xxxFastLoadLSecurityDescriptions(session: URLSession, mySymbols: [String]?, completion: @escaping(Bool) ->()) {
let myGroup = DispatchGroup()
for symbolIndex in 0..<(mySymbols?.count ?? 0) {
myGroup.enter()
mySessionSendMGBGenericRequest(...) { [weak self] returnValue in
guard let self = self else { myGroup.leave() ; return }
// do some stuff with returnValue
myGroup.leave()
}
}
myGroup.notify(queue: .main, execute: {
completion(true)
})
}