ЦП достигает 100%, когда свойство объекта установлено двумя другими объектами одновременно - PullRequest
0 голосов
/ 13 июня 2019

Я имею дело с особенно неприятной ошибкой, которая приводит к тому, что основной поток достигает% 100, а пользовательский интерфейс полностью зависает.

По сути, это «симуляция», в которой 100 экземпляров узлов создаются как «ячейки», а 2 других узла - как атакующий и защитник.Ячейки имеют два свойства, зависимость и раздражение, которые изменяются атакующим и защитником каждую секунду.

Я запускаю таймер, который вызывает метод fire () для каждого из нападавших и защитника, пропуская случайно выбранную ячейкув качестве аргумента.

Метод fire () в атакующем будет рассчитывать свою собственную мощность на основе времени T, которое является временем, прошедшим с момента запуска симуляции, и вызывать методы IncreaseAddiction () и IncreaseAnnoyance () для переданных ячеек,прохождение это рассчитанная мощность.То же самое произойдет с методом fire () ячейки защитника, но на этот раз вызываются методы decaeAddiction () и lowerAnnoyance ().

Расчет мощности не является линейной функцией и отличается для атакующего иЗащитник создает моменты доминирования, для атакующего или защитника.Узлы ячеек окрашены в соответствии с их уровнями зависимости и раздражения, что делает их интересной анимацией

Проблема появляется через некоторое время T после начала симуляции.Мне удалось выделить строку «self.annoyanceLevel = newLevel» в качестве виновника, когда удаленный код не показывает это поведение.Кроме того, проблема не возникает при удалении методов fire () у атакующего или защитника, что привело меня к подозрению о состоянии гонки, поэтому я пытался вызывать их по очереди, а не вызывать их одновременно, но это не помогло.Я пытался создать новую очередь отправки и вызывать их оттуда, но это не помогло.Я пытался создать механизм блокировки / разблокировки, помечая ячейку как «inUse» до тех пор, пока setAnnoyance () не вернется, но это не помогло, я попытался поместить изменения в очередь, добавив их в массив и вызвав функцию setAnnoyanceLevels () для отдельного использованиязначения в массиве que но это не помогло.Я не смог найти ничего подобного делению на ноль или другим логическим проблемам, таким как случайные бесконечные циклы.

Я пытался жестко закодировать значение, которое вызывает проблему (по-видимому, оно зависает при изменении с 63 на 100), и это решило проблему, однако, это не помогает мне, так как мне не нужно жестко закодированное число,

Вот упрощенные версии функций

Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(fireAtCells), userInfo: nil, repeats: true)


 /* The methods that run the simulation */
 @objc func fireAtCells(){

         for bot in  GameEngine().data.attackBots{

            var targetCells =  GameEngine().data.cells.filter { cell in
                return cell.addictionLevel < 100
            }


                let pickedCell = Int.random(in: 0 ... targetCells.count - 1)


                bot.fire(cell: targetCells[pickedCell])
            }
        }
          for bot in  GameEngine().data.defenceBots{

                var targetCells =  GameEngine().data.cells.filter { cell in
                    return cell.addictionLevel > 0 || cell.annoyanceLevel > 0
                }


                    let pickedCell = Int.random(in: 0 ... targetCells.count - 1)


                    bot.fire(cell: targetCells[pickedCell])
                }
            }


    }

 /* These are the methods on the attacker and defender that calculate the power etc. and increaseAddicton  or decreaseAnnoyance, decreaseAddiction methods on the Cells */

 func fire(cell: Cell){

     let gameStatus = GameEngine()

    let pickBullet = Int.random(in: 0 ... bullets.count - 1)
    let bullet = bullets[pickBullet]

        switch bullet {
        case .addict:
            cell.decreaseAnnoyance(50)
            cell.increaseAddiction(Int(gameStatus.status.attackPower))
            bulletColor = .red
        case .annoy:
            let power = Int(gameStatus.status.attackPower)
            cell.increaseAnnoyance(power)

        }


}

 /* The methods in the Cell objects that adjust it's own properties when called from the attacker or defender, addiction methods are pretty much the same, so ommiting them */

 func increaseAnnoyance(_ power: Int){
     self.setAnnoyanceLevel(power + self.annoyanceLevel)
 }
 func decreaseAnnoyance(_ power: Int){
     self.setAnnoyanceLevel(self.annoyanceLevel - power)
 }



 func setAnnoyanceLevel(_ level : Int){

        let newLevel = (level > 100) ? 100 : (level < 1 ? 1 : level)

        self.annoyanceLevel = newLevel 


        self.wiggle(interval: 0.5 - 0.004 * Double(level), strength: 20)
        self.colorize(color: UIColor.yellow, level: level)

    }

enter image description here

enter image description here

enter image description here

Обновление: Мне удалось перехватить выполнение незадолго до того, как процессор достигнет% 100, и похоже, что таймер плохо себя ведет.После того, как incrementAnoyance () возвращается и таймер готовится к запуску метода FireAtCells (), он каким-то образом застревает в бесконечном цикле.В последнем цикле перед выпуском% 100 я вижу, что объект Timer фактически никогда не завершает инициализацию и не вызывает метод ForeAtCells, вместо этого он застревает.

Обновление 2 Хорошо, страннодостаточно, таймер никогда не завершает выполнение в последний раз, но кажется, что ЦП% 100 вызван таймером, используемым некоторыми SKAction на узлах Spritekit.Действия SpriteKit никогда не обращаются к этим свойствам, я не понимаю, почему проблема возникает только тогда, когда свойство объекта изменяется программно.

1 Ответ

0 голосов
/ 13 июня 2019

Попробуйте, если устраняет двойной цикл и удаляет таймер:

let action = SKAction.repeatForever(SKAction.sequence([SKAction.run{fireAtCells()},SKAction.wait(forDuration:0.2)]))
scene.run(action)


/* The methods that run the simulation */
@objc func fireAtCells(){
    var attackTargetCells =  GameEngine().data.cells.filter { cell in
        return cell.addictionLevel < 100
    }
    for bot in  GameEngine().data.attackBots{
        let pickedCell = attackTargetCells[Int.random(in: 0 ... attackTargetCells.count - 1)]
        bot.fire(cell: pickedCell)
        if pickedCell.addictionLevel >= 100 { 
            attackTargetCells.remove(pickedCell)
        }
    }
    let defenceTargetCells =  GameEngine().data.cells.filter { cell in
        return cell.addictionLevel > 0 || cell.annoyanceLevel > 0
    }
    for bot in  GameEngine().data.defenceBots{
        let pickedCell = defenseTargetCells[Int.random(in: 0 ... defenseTargetCells.count - 1)]
        bot.fire(cell: pickedCell)
        if cell.addictionLevel <= 0 || cell.annoyanceLevel <= 0 {
            defenseTargetCells.remove(pickedCell)
        }
    }
}

 /* These are the methods on the attacker and defender that calculate the power etc. and increaseAddicton  or decreaseAnnoyance, decreaseAddiction methods on the Cells */

func fire(cell: Cell){
    let gameStatus = GameEngine()
    let pickBullet = Int.random(in: 0 ... bullets.count - 1)
    let bullet = bullets[pickBullet]
        switch bullet {
        case .addict:
            cell.decreaseAnnoyance(50)
            cell.increaseAddiction(Int(gameStatus.status.attackPower))
            bulletColor = .red
        case .annoy:
            let power = Int(gameStatus.status.attackPower)
            cell.increaseAnnoyance(power)

        }
}

 /* The methods in the Cell objects that adjust it's own properties when called from the attacker or defender, addiction methods are pretty much the same, so ommiting them */

 func increaseAnnoyance(_ power: Int){
     self.setAnnoyanceLevel(power + self.annoyanceLevel)
 }
 func decreaseAnnoyance(_ power: Int){
     self.setAnnoyanceLevel(self.annoyanceLevel - power)
 }



 func setAnnoyanceLevel(_ level : Int){
     let newLevel = (level > 100) ? 100 : (level < 1 ? 1 : level)
     self.annoyanceLevel = newLevel 
     self.wiggle(interval: 0.5 - 0.004 * Double(level), strength: 20)
     self.colorize(color: UIColor.yellow, level: level)
 }
...