我有一个非常简单的服务器(使用 kryonet)。客户端仅存储汽车的当前状态(x、y、角度等)并发送加速和转向请求。
服务器正在接收请求并将它们添加到物理线程耗尽、读取和更新的 ArrayBlockingQueue 中。
当添加另一个玩家时,游戏的速度会减慢几乎一倍。我已经排除了很多事情(我将所有更新和包发送限制在 60Hz。)
我怀疑使用阻塞队列会阻塞太多以至于导致速度变慢。
如何在没有阻塞问题的情况下将客户端请求发送到物理线程?
我有一个非常简单的服务器(使用 kryonet)。客户端仅存储汽车的当前状态(x、y、角度等)并发送加速和转向请求。
服务器正在接收请求并将它们添加到物理线程耗尽、读取和更新的 ArrayBlockingQueue 中。
当添加另一个玩家时,游戏的速度会减慢几乎一倍。我已经排除了很多事情(我将所有更新和包发送限制在 60Hz。)
我怀疑使用阻塞队列会阻塞太多以至于导致速度变慢。
如何在没有阻塞问题的情况下将客户端请求发送到物理线程?
我怀疑使用阻塞队列会阻塞太多以至于导致速度变慢。
你怀疑错了。以下测试程序通过 ArrayBlockingQueue 推送 100 万个整数:
public class ArrayBlockingQueuePerfTest {
int maxi = 1000000;
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(1000,
true);
Thread sender = new Thread("sender") {
public void run() {
try {
for (int i = 0; i < maxi; i++) {
queue.offer(i, 1, TimeUnit.SECONDS);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
};
Thread receiver = new Thread("receiver") {
public void run() {
try {
int count = 0;
long sum = 0;
while (count < maxi) {
sum += queue.poll(1, TimeUnit.SECONDS);
count++;
}
System.out.println("done");
System.out.println("expected sum: " + ((long) maxi) * (maxi - 1) / 2);
System.out.println("actual sum: " + sum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
};
public ArrayBlockingQueuePerfTest() {
sender.start();
receiver.start();
}
public static void main(String[] args) {
new ArrayBlockingQueuePerfTest();
}
}
在我的笔记本电脑上,它会在几秒钟内终止。因此,无论您的性能瓶颈在哪里,它都不是 ArrayBlockingQueue,它可以处理比您需要的吞吐量至少高 3 个数量级的吞吐量。换句话说,即使您找到了一种完全不占用执行时间的线程通信方法,也最多只能将您的程序加速 0.1%。
把这个问题和所有其他性能问题带回家:解决现有代码中的任何性能问题的第一步是衡量代码的哪一部分是慢的,通常情况下,它不是人们所期望的。探查器大大简化了这项任务。
您可以使用中断器(环形缓冲区),这是一种用于实现队列的无锁机制。看:
我发现了这个错误。我需要以不同的方式限制物理模拟(不是使用 world.step() 函数,而是限制调用的频率)。是这样的。
while(true)
{
delta = System.nanoTime() - timer;
if(delta >= 16666666) // 60 Hz
{
world.step(1.0f, 6, 2);
processAndUpdateYourData();
timer = System.nanoTime();
}
}
然后我需要调整所有物理数字,使它们在这种配置下感觉自然。
您的问题以实现为前提-您最好问“为什么我的代码这么慢?”。
阻塞队列是实现生产者/消费者模式的最有效方式。
我会添加更多的消费者线程 - 尝试添加与处理器内核一样多的消费者线程 - 即Runtime.getRuntime().availableProcessors()
。
确保您创建的队列有足够的可用插槽来满足所有客户端。如果队列因为客户端太多而变满,它们会在尝试插入命令时阻塞。如果这不是问题,则意味着您的物理(消费者)线程没有跟上请求,您需要确保它获得更多处理时间。