在多线程环境中限制方法调用/秒
Limit method call / seconds in multi-threaded environnement
我正在开发处理游戏武器的机制。
我在射速方面遇到了问题,它在单线程下运行良好,但似乎我的 GUI 正在从多线程调用 fire() 方法,这有时会导致一些意外行为(多次射击非常快)。
所以我在方法中添加了一个锁(带有 AtomicBoolean
标志),但它只会导致错误出现的频率降低。
武器class
private final AtomicBoolean locked = new AtomicBoolean(false);
private final AtomicInteger fired = new AtomicInteger(0);
@Override
// returns true if a shot was fired; false otherwise
public boolean fire() {
if (locked.compareAndSet(false, true)) {
return false;
}
if (!canFire || reloading)
return false;
if (bulletsInMagazine > 0) {
canFire = false;
timeSinceLastShot = 0;
bulletsInMagazine--;
fired.incrementAndGet();
if (bulletsInMagazine == 0) {
reload();
}
locked.set(false);
return true;
}
reload();
locked.set(false);
return false;
}
@Override
public void tick(int ms) {
timeSinceLastShot += ms;
if (timeSinceLastShot >= delayBetweenShotsMs) {
canFire = true;
}
remainingReloadTime = Math.max(0, remainingReloadTime - ms);
if (remainingReloadTime == 0 && reloading)
processReloading();
}
我写了一个重现问题的单元测试...有时。
测试:
@Test
public void testFiringRate() {
int nbBullets = weapon.getBulletsInMagazine();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(16);
for (int i = 0; i < 16; i++) {
scheduledExecutorService.scheduleAtFixedRate(weapon::fire, 5, 10, TimeUnit.MILLISECONDS);
}
System.out.println("START firing");
int ticks = 0;
while (ticks < 100) {
weapon.tick(100);
ticks++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
int fired = nbBullets - weapon.getBulletsInMagazine();
int reallyFired = weapon.getFiredShotCount();
int diff = reallyFired - fired;
if (diff != 0) {
System.out.println(ticks + "\t" + fired + " shot(s)\t" + reallyFired + " really fired\t" + (diff != 0 ? "(+" + diff + ")" : ""));
} else {
System.out.print(".");
}
}
System.out.println("\nSTOP firing");
System.out.println(ticks + " Ticks");
try {
System.out.print("WAITING FOR ALL TASKS TO TERMINATE...");
scheduledExecutorService.shutdown();
scheduledExecutorService.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("DONE.");
} catch (InterruptedException e) {
e.printStackTrace();
}
int bulletsAfter = weapon.getBulletsInMagazine();
int fired = nbBullets - bulletsAfter;
System.out.println("Shot fired: " + fired);
int shotsFired = weapon.getFiredShotCount();
System.out.println("Really fired: " + shotsFired);
assertEquals(shotsFired, fired);
}
我得到的(有时):
计算出的镜头比实际少1或2个(来自AtomicInteger
fired)
我想要的:
无论线程数如何,武器都应尊重其射速。
有什么想法吗?
编辑:
我试图反转 if 条件:
if (!locked.compareAndSet(false, true)) {
return false;
}
它解决了单元测试的问题,但现在 GUI 只触发一次,再也不会触发。
我按照 的建议将方法主体移动到 synchronized(this)
块中并且它起作用了,因为我在多次尝试后无法重现错误。
我正在开发处理游戏武器的机制。
我在射速方面遇到了问题,它在单线程下运行良好,但似乎我的 GUI 正在从多线程调用 fire() 方法,这有时会导致一些意外行为(多次射击非常快)。
所以我在方法中添加了一个锁(带有 AtomicBoolean
标志),但它只会导致错误出现的频率降低。
武器class
private final AtomicBoolean locked = new AtomicBoolean(false);
private final AtomicInteger fired = new AtomicInteger(0);
@Override
// returns true if a shot was fired; false otherwise
public boolean fire() {
if (locked.compareAndSet(false, true)) {
return false;
}
if (!canFire || reloading)
return false;
if (bulletsInMagazine > 0) {
canFire = false;
timeSinceLastShot = 0;
bulletsInMagazine--;
fired.incrementAndGet();
if (bulletsInMagazine == 0) {
reload();
}
locked.set(false);
return true;
}
reload();
locked.set(false);
return false;
}
@Override
public void tick(int ms) {
timeSinceLastShot += ms;
if (timeSinceLastShot >= delayBetweenShotsMs) {
canFire = true;
}
remainingReloadTime = Math.max(0, remainingReloadTime - ms);
if (remainingReloadTime == 0 && reloading)
processReloading();
}
我写了一个重现问题的单元测试...有时。
测试:
@Test
public void testFiringRate() {
int nbBullets = weapon.getBulletsInMagazine();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(16);
for (int i = 0; i < 16; i++) {
scheduledExecutorService.scheduleAtFixedRate(weapon::fire, 5, 10, TimeUnit.MILLISECONDS);
}
System.out.println("START firing");
int ticks = 0;
while (ticks < 100) {
weapon.tick(100);
ticks++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
int fired = nbBullets - weapon.getBulletsInMagazine();
int reallyFired = weapon.getFiredShotCount();
int diff = reallyFired - fired;
if (diff != 0) {
System.out.println(ticks + "\t" + fired + " shot(s)\t" + reallyFired + " really fired\t" + (diff != 0 ? "(+" + diff + ")" : ""));
} else {
System.out.print(".");
}
}
System.out.println("\nSTOP firing");
System.out.println(ticks + " Ticks");
try {
System.out.print("WAITING FOR ALL TASKS TO TERMINATE...");
scheduledExecutorService.shutdown();
scheduledExecutorService.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("DONE.");
} catch (InterruptedException e) {
e.printStackTrace();
}
int bulletsAfter = weapon.getBulletsInMagazine();
int fired = nbBullets - bulletsAfter;
System.out.println("Shot fired: " + fired);
int shotsFired = weapon.getFiredShotCount();
System.out.println("Really fired: " + shotsFired);
assertEquals(shotsFired, fired);
}
我得到的(有时):
计算出的镜头比实际少1或2个(来自AtomicInteger
fired)
我想要的: 无论线程数如何,武器都应尊重其射速。
有什么想法吗?
编辑:
我试图反转 if 条件:
if (!locked.compareAndSet(false, true)) {
return false;
}
它解决了单元测试的问题,但现在 GUI 只触发一次,再也不会触发。
我按照 synchronized(this)
块中并且它起作用了,因为我在多次尝试后无法重现错误。