Наиболее эффективная реализация потоков IOS

#ios #multithreading #core-audio

#iOS #Многопоточность #ядро-аудио

Вопрос:

Я пытался найти реализацию потоков в IOS, которая соответствует потребностям моих проектов. До сих пор мне не удалось найти приемлемое решение.

Моя проблема :

Мне нужно одновременно считывать аудио из 16 файлов mp3 на диске.

Что я пробовал:

Во-первых, я попытался использовать NSTimer, который повторяется. Таймер работал недостаточно быстро, и звук пропадал, когда я воспроизводил более 4 файлов.

Во-вторых, я попытался использовать NSThread с приоритетом 1. Звук почти воспроизводился правильно, но пользовательский интерфейс перестал отвечать на запросы.

Наконец, я попытался отправлять блоки с помощью GCD в моем обратном вызове всякий раз, когда мне нужно было больше звука из файла. Снова звук пропадал, но пользовательский интерфейс был отзывчивым.

Во всех трех приведенных выше примерах я также пытался разделить рабочую нагрузку, создав 4 потока и заставив каждый поток обрабатывать по 4 аудиофайла, но это вызвало действительно серьезные проблемы с синхронизацией звука.

Существуют ли другие варианты потоков, которые я могу попробовать, или сделать вышеприведенный итог того, что может предложить IOS?

Считаете ли вы, что одновременное чтение 16 файлов с диска является слишком большой нагрузкой для системы IOS?

Существует ли ограничение на количество потоков, которые может обрабатывать IOS?

Чтобы мой вопрос не звучал как обсуждение, я резюмирую следующим образом.

Какая технология потоков IOS лучше всего подходит для очень частых вызовов, быстрого завершения выполнения, которая может быть легко синхронизирована и не повлияет на отзывчивость пользовательского интерфейса.

Также приветствуется любой случайный совет по решению аналогичной проблемы с программированием звука.

РЕДАКТИРОВАТЬ 1

Это несколько сокращенный код, который я смоделировал по предложению пользователя so. Все, что мне нужно, — это твердый совет о том, какая настройка будет работать лучше всего для меня. Со времени моего последнего поста я попробовал NSThread, и, похоже, у меня пропадает звук. Также я попытался использовать NSConditions, чтобы мой поток тратил вычислительную мощность, когда он не заполняет буфер, но использование этих блокировок кажется очень плохой идеей для обратных вызовов аудио.

 OSStatus channelMixerCallback(void *inRefCon, 
                              AudioUnitRenderActionFlags *ioActionFlags, 
                              const AudioTimeStamp *inTimeStamp, 
                              UInt32 inBusNumber, 
                              UInt32 inNumberFrames, 
                              AudioBufferList *ioData) { 


    AudioInfo = myaudio[inBusNumber];

    if(myaudio.needsbufferfill==YES)

    {
        [refToSelf performSelector:@selector(GetAudioForItem:) onThread:engineDescribtion.producerthread withObject:myaudio waitUntilDone:false];

    }





}

-(void) startthread

{
    engineDescribtion.producerthread =[[NSThread alloc]initWithTarget:self selector:@selector(dosinglerunloop) object:nil];

    [engineDescribtion.producerthread start];



}

-(void)dosinglerunloop
{
    BOOL isstarted=YES;
    NSAutoreleasePool *pool=[[NSAutoreleasePool alloc]init];

    do {
        [[NSRunLoop currentRunLoop]addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

    } while (isstarted);


    [pool release];

}


- (void)GetAudioForItem:(AudioInfo *)info
{

    // use data in Audio Info to seek to
    //corrent place in file
    //and extract audio to buffers

}
  

Ответ №1:

Проблема 0:

Ваши обратные вызовы аудио-рендеринга никогда не должны блокироваться. Пример: создание одного выделения кучи приведет к блокировке.


Все ваши потоки будут конкурировать за аппаратное обеспечение. Чтобы пользовательский интерфейс оставался отзывчивым, у вас не должно быть много потоков с наивысшим приоритетом (воспроизведение звука должно быть единственным). Учитывайте количество ядер, дисков и т. Д., Доступных в вашем проекте.

Если у вас все еще есть проблемы после того, как вы правильно исправили это: загрузка коротких файлов в память может разгрузить часть потребности диска в памяти.

Вы должны профилировать, чтобы определить, в чем на самом деле проблема: это может быть процессор или ввод-вывод. Возможно, вы просто пропускаете сроки рендеринга и приравниваете выпадающие аудио к «не могу читать достаточно быстро». Если вы используете много процессора, то дисковый ввод-вывод может быть не проблемой. Для декодирования и выполнения преобразования частоты дискретизации 16 файлов mp3 может потребоваться относительно высокий процессор (как один из примеров того, что вам нужно искать).

pthreads будет самым быстрым, но потребует некоторой работы для правильной реализации. На данный момент это действительно не имеет значения, потому что, похоже, пока есть несколько проблем высокого уровня, и существует несколько API, которые должны отлично справляться с этой задачей.

Ваша программа должна быть достаточно умной, чтобы определять, когда буферы чтения не могут быть заполнены достаточно быстро.

Вы предварительно заполняете буферы, правильно?

Предположительно, вы используете цикл выполнения?

Комментарии:

1. не могли бы вы объяснить мне «выделение одной кучи»?

2. @dubbeat void* a = malloc(1); и NSObject * obj = [[NSObject alloc] init]; оба являются примерами вещей, которые создают выделение кучи.

3. Да, я предварительно заполняю буферы. Каждый буфер разбит на 2 части, A и B. A заполняется во время чтения B, а B заполняется во время чтения A. (Я не смог правильно реализовать CARingbuffer) Размер буфера равен 100 * количеству кадров обратного вызова (512). Я никогда не использовал цикл выполнения для этого, только методы, которые я описал выше.

4. Циклы выполнения @dubbeat можно использовать для приостановки выполнения потока чтения, когда чтение не выполняется (читайте: если он всегда выполнялся, вы могли бы перезаписать данные, которые вы подготовили, но еще не воспроизвели). Мне не нравятся общедоступные API-интерфейсы CA, но я сам решил большинство этих проблем. Учитывая ваш опыт, я бы рекомендовал вам использовать их (например CARingBuffer ) там, где это применимо, вместо того, чтобы изобретать концепции, на освоение и внедрение которых уходит несколько лет. Используйте их повторно в своих программах, но не считайте их хорошей моделью для изучения, если вы хотите писать современные звуковые проекты на C .

Ответ №2:

Ну, есть только один диск… Таким образом, любое решение, требующее одновременного чтения 16, может быть проблемой. (В зависимости от того, привязан ли вы к вводу-выводу или к процессору.)

NSTimer не даст вам согласованных результатов.

Я не вижу никаких причин, по которым NSThread снизил бы отзывчивость пользовательского интерфейса, возможно, у вас была ошибка.

Комментарии:

1. Если NSThread очень интенсивно использует процессор и требует много времени для выполнения, может ли это потенциально отнять время обработки у основного потока? Я использовал detatchnewthreadselector с приоритетом 1.0

2. Ну, конечно, любой поток, который обрабатывает, требует времени от основного потока. Но основному потоку не должно требоваться 100% процессора, чтобы оставаться отзывчивым. Проблема может быть в чрезмерной приоритизации, я с этим не играл. Я сомневаюсь, что один поток перегружает поток пользовательского интерфейса.

Ответ №3:

Я использую привязку этой системы к диску, потому что 16 каналов MP3 не являются проблемой для процессора на современных машинах — сколько шума исходит из вашего ящика? У меня, вероятно, возникнет соблазн использовать только один поток для заполнения пустых буферов буфером размером, соответствующим размеру (averageDiskLatency * (байт / мс) * 16 * bodgeFactor) байт аудиопотока, (bodgeFactor означает округление до границы 8 КБ и добавление нескольких 8 КБ). Всякий раз, когда потоки / обратные вызовы / что угодно очищают буфер и, следовательно, запускаются с другого, они должны помещать пустой буфер в поток чтения с диска (потокобезопасная очередь производитель-потребитель), чтобы он снова заполнился. Вероятно, каждый буфер должен включать экземпляр ‘fileControl’, содержащий спецификацию файла, дескриптор файла, переменную состояния для EOF и т. Д., Пространство строк ошибок и все остальное, необходимое для работы потока чтения, а также само буферное пространство.

Такая конструкция позволяет диску считывать хорошие, большие куски, не будучи раздражающе вытесненным на полпути при чтении и не будучи вынужденным слишком часто перемещать куски металла.

Rgds, Мартин

PS — Если у вас его еще нет, приобретите SSD-накопитель — творит чудеса с задержкой многоканального аудио / видео.

Комментарии:

1. @Martin. Я отредактировал свой вопрос с помощью некоторого урезанного кода, который, я думаю, основан на вашем предложении. Не могли бы вы посоветовать мне, если это то, что вы имели в виду? (У меня пока нет рабочего решения)