Двухмерные изменчивые массивы: поможет ли самостоятельное назначение или мне нужен AtomicIntegerArray?
пишу аудио приложение DSP и яМы решили использовать модель производитель-потребитель. Я'много читал оvolatile
и другие вопросы, связанные с потоками, но яУ меня есть пара вопросов о некоторых особенностях моего случая - в частности, одна из вещей, которую мне нужно разделить между потоками, - это массив массивов.
У меня есть класс, который представляет производителя. Чтобы учесть разницу во времени обработки, производитель хранитn
буферы, которые будут заполняться по очереди каждый раз, когда будет доступно больше аудиоданных, и передавать буфер в поток потребителя.
начну с моих вопросов, а потомЯ постараюсь объяснить мою систему достаточно подробно - извините за длинный пост, спасибо за терпение со мной! Я'Буду также очень благодарен за общие комментарии о моей реализации и безопасности ее потоков.
Мои буферы представленыvolatile byte[][]
массив. Я хорошо знаю, чтоvolatile
только делаетссылка волатильно, но, прочитав SO и различные посты в блоге, кажется, у меня есть два варианта:
Я мог бы использоватьAtomicIntegerArray
, Но:
Буду ли я ставить под угрозу производительность для такого приложения?
Атомность даже то, что мне нужно? Я намерен записать весь массив за один раз,затем Мне нужно, чтобы это было видно в другой теме, я некаждый нужениндивидуальный написать, чтобы быть атомным или видимым немедленно.
Если я правильно понимаю (например,этот блог), самостоятельное назначение, которое в моем случае:buffers[currentBuffer] = buffers[currentBuffer]
обеспечит публикацию, которую вы увидите в моем коде ниже.
Это правильно, что это приведет к тому, что все последние записи станут видимыми?
Работает ли это в случае двумерного массива, как этот?
попробую кратко рассмотреть класс продюсера; это переменные экземпляра:
// The consumer - just an interface with a process(byte[]) method
AudioInputConsumer consumer;
// The audio data source
AudioSource source;
// The number of buffers
int bufferCount;
// Controls the main producer loop
volatile boolean isRunning = false;
// The actual buffers
volatile byte[][] buffers;
// The number of buffers left to process.
// Shared counter - the producer inrements and checks it has not run
// out of buffers, while the consumer decremenets when it processes a buffer
AtomicInteger buffersToProcess = new AtomicInteger(0);
// The producer thread.
Thread producerThread;
// The consumer thread.
Thread consumerThread;
Как только я начнуproducerThread
а такжеconsumerThread
они просто выполняют методыproducerLoop
а такжеconsumerLoop
соответственно.
producerLoop
блокирует в ожидании аудиоданных, читает в буфер,выполняет самостоятельное назначение в буферезатем используетAtomicInteger
экземпляр для подачи сигнала в потребительский цикл.
private void producerLoop() {
int bufferSize = source.getBufferSize();
int currentBuffer = 0;
while (isRunning) {
if (buffersToProcess.get() == bufferCount) {
//This thread must be faster than the processing thread, we have run out
// of buffers: decide what to do
System.err.println("WARNING: run out of buffers");
}
source.read(buffers[currentBuffer], 0, bufferSize); // Read data into the buffer
buffers[currentBuffer] = buffers[currentBuffer]; // Self-assignment to force publication (?)
buffersToProcess.incrementAndGet(); // Signal to the other thread that there is data to read
currentBuffer = (currentBuffer + 1) % bufferCount; // Next buffer
}
}
consumerLoop
ждет, покаAtomicInteger
buffersToProcess
больше нуля, затем вызывает потребительский объект, чтобы делать с данными все, что он хочет. потомbuffersToProcess
уменьшается, и мы ждем, пока оно снова станет ненулевым.
private void consumerLoop() {
int currentBuffer = 0;
while (isRunning) {
if (buffersToProcess.get() > 0) {
consumer.process(buffers[currentBuffer]); // Process the data
buffersToProcess.decrementAndGet(); // Signal that we are done with this buffer
currentBuffer = (currentBuffer + 1) % bufferCount; // Next buffer
}
Thread.yield();
}
}
Большое спасибо!