Как я могу передавать сообщения в другой поток без использования очереди блокировки?
У меня довольно простой сервер (использующий крионет). Клиенты хранят только текущее состояние автомобиля (x,y, угол и т. Д.) И отправляют запросы на ускорение и поворот.
Сервер получает запросы и добавляет их в ArrayBlockingQueue, который физический поток истощает, читает и обновляет.
При добавлении другого игрока скорость игры замедляется почти вдвое. Я исключил много вещей (у меня все обновления и посылка посажены на частоте 60 Гц.)
Я подозреваю, что использование очереди блокировки блокирует настолько, что вызывает замедление.
Как я могу отправлять клиентские запросы в физический поток, не блокируя проблемы?
5 ответов
Я нашел ошибку. Мне нужно было регулировать физическое моделирование другим способом (не с помощью функции world.step(), но ограничивать частоту его вызова). Это что-то вроде этого.
while(true)
{
delta = System.nanoTime() - timer;
if(delta >= 16666666) // 60 Hz
{
world.step(1.0f, 6, 2);
processAndUpdateYourData();
timer = System.nanoTime();
}
}
Затем мне нужно отрегулировать все физические числа, чтобы они выглядели естественно при такой конфигурации.
Я подозреваю, что использование очереди блокировки блокирует настолько, что вызывает замедление.
Вы подозреваете, что это неправильно. Следующая тестовая программа проталкивает 1 миллион целых чисел через ArrayBlockingQueue:
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%.
Возьмите домашний урок для этой и всех других проблем с производительностью. Первый шаг при решении любой проблемы с производительностью в существующем коде - это определить, какая часть кода медленная, как обычно, это не то, чего ожидают. Профилировщики значительно упрощают эту задачу.
Вы можете использовать разрушитель (кольцевой буфер), механизм без блокировки для реализации очереди. Увидеть:
Убедитесь, что вы создаете очередь с достаточным количеством доступных слотов, чтобы удовлетворить всех клиентов. Если очередь заполнится из-за слишком большого количества клиентов, они будут блокироваться при попытке вставить команды. Если это не проблема, это означает, что ваш физический (потребительский) поток не поспевает за запросами, и вам нужно убедиться, что он получает больше времени на обработку.
Ваш вопрос предполагает реализацию - вам лучше спросить "почему мой код такой медленный?".
Очередь блокировки является наиболее эффективным способом реализации шаблона производитель / потребитель.
Я хотел бы добавить больше потребительских потоков - попробуйте добавить столько потребительских потоков, сколько есть процессорных ядер - т.е. Runtime.getRuntime().availableProcessors()
,