Оптимизация производительности — устранение неполадок, блокирующих выполнение подпроцесса JAVA.

Java
Оптимизация производительности — устранение неполадок, блокирующих выполнение подпроцесса JAVA.

1. Описание проблемы

1.1 Связанная информация

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

                                

Мы используем cmd для вызова класса библиотеки ffmpeg, который реализуется командой ffprob.Выходной поток данных записывается в файл на диске посредством межпроцессного взаимодействия, а затем загружается в облачное хранилище.Конкретный процесс показан слева Справа показан сгенерированный файловый поток потока данных.

      

1.2 Проблемы возникают

Конкретная логика вызова показана на рисунке ниже.Весь процесс выполняется в виде асинхронного выполнения задачи пула потоков.В то время параметры пула потоков обработки задачи видеоизображения:coreSize = 30, maxSize = 40**,** емкость очереди = 50.Однако при работе с обычным видео иногда будут возникать сбои в обработке задач.Пользователей этой функции не так много.В то время я подумал, что лучше локализовать проблему. Глядя на журнал, вы можете видеть, что поток задачи иногда зависает в ожидании выхода дочернего процесса в запущенном дочернем процессе ffmpeg. Но эта проблема воспроизводится, когда для вызова используется локальный цикл, и информация о стеке в это время выглядит следующим образом: вы можете видеть, что поток находился в состоянии TIMED_WAITING. Основываясь на этой ситуации, я добавил несколько журналов и обнаружил, что он окончательно застрял в методе process.wait().

"ffmpeg process" daemon prio=10 tid=0x00006f52904dc000 nid=0x14bf waiting on condition [0x00007f427aa2f000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to waeit for  <0x00000009c229ebf1> (a java.util.concurrent.SynchronousQueue$TransferStack)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
    at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
    at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:359)
    at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:942)
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1043)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1103)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722) 

2. Анализ проблемы и решение

В то время я искал проблему «java выполняет блокировку cmd» и нашел несколько ответов.Process.waitFor() будет втекущий потокБлокировка и ожидание, если необходимо, он будет ждать, пока процесс, представленный объектом Process, не завершится.Если вы не будете осторожны при вызове этого метода, то легко заблокируете основной поток, и процесс также зависнет.. При вызове waitFor() Process должен сообщить о статусе выполнения основному потоку, поэтому обратите внимание на очистку буферов, а именно InputStream и ErrorStream. Мы экспериментировали локально, на этот раз вообще не читая выходной поток подпроцесса. Разумеется, вероятность случайного появления основного процесса на этот раз гораздо выше.

 for (int i = 0; i < 200; i++) {        
     //执行ffmpeg,会输出很多数据流
     Process process = Runtime.getRuntime().exec("..."); 
     Thread.sleep(5000);         
     int status = process.waitFor();            
     System.out.println(status);
     System.out.println(i);
}

 for (int i = 0; i < 200; i++) {        
     //执行ffmpeg,会输出很多数据流
     Process process = Runtime.getRuntime().exec("..."); 
     //启动单独线程读取outputStream
     new Thread(()->{读取outputStream;}).start();
     //启动单独线程读取errorStream
     new Thread(()->{读取errorStream;}).start();
     Thread.sleep(5000);         
     int status = process.waitFor();            
     System.out.println(status);
     System.out.println(i);
}

В настоящее время мы можем знать, что мы должны постараться не допустить зависания дочернего процесса и дать возможность потоку данных, выводимому в буфер, быть прочитанным вовремя, В этом случае задача не может быть помещена в очередь задач пула потоков, что приведет к зависанию дочернего процесса с высокой вероятностью. , мы напрямую открываем пул потоков, предназначенный для обработки потоков данных, убиваем очередь ожидания задач и пытаемся настроить количество потоков обработки так, чтобы оно удвоило количество одновременных запросов на обработку видео.coreSize = 60, maxSize = 80, queueCapacity = 0, **** пусть пул потоковКак можно скорее обработайте выходные данные процесса в буфер. Запустил тест несколько раз снова, и проблема не возникла.

3. Принцип задачи

3.1 Знания, связанные с процессом

В процессе есть несколько потоков, которые могут совместно использовать одно и то же адресное пространство пользователя.Обычно мы используем более общие потоки между потоками, такие какПеременная условия мьютекса, блокировка чтения-записи, семафори другие механизмы связи, так как каждый процесс имеет разное пользовательское адресное пространство, глобальные переменные любого процесса нельзя увидеть в другом процессе, поэтому для обмена данными между процессами необходимо пройти через ядро ​​и открыть буфер в ядре. процесс 1 копирует данные из пространства пользователя в буфер ядра, а затем процесс 2 копирует данные из буфера ядра в пространство пользователя, включаяканалы/сокеты/общая память/очереди сообщений/семафорыи другие средства связи.

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

Видно, что связь между процессом выполнения и процессом в java реализована через Process/UNIXProcess/ProcessImpl/ProcessBuilder и связанные с ними классы.

При создании нового экземпляра процесса выходной поток будет инициализирован с помощью метода Start() класса ProcessImpl, а затем будет создан новый объект экземпляра UNIXProcess.

Видно, что в классе UNIXProcess вызывается метод forkAndExec() класса, который вызывает родной метод forkAndExec() после инициализации коммуникационного конвейера.

Здесь запускается пул потоков для выполнения операции waitForProcessExit() Этот метод блокируется и ожидает, и он будет ждать завершения процесса перед завершением.

3.2 Знания о конвейерах

Это скопировано из исходного кода ядра Linux.Чтение и запись конвейера достигается за счет работы с одним и тем же буфером.Перед записью выполняется блокировка синхронизации, а затем выполняется соответствующая операция в соответствии с количеством бит, которые можно записать в буфер.

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

4. Резюме

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

использованная литература

woohoo.coder.work/article/182…

Дэвид Луо, Цао Цао/2016/08/01/…Реализация канала Linux

blog.CSDN.net/Нет любви и щепотки…

Wooooooo.. Есть так много.com/A/A2 не 3JL OJ…

blog.CSDN.net/Мини-тело Дисы…

GitHub.com/морской Уокер/…

code.i erection.org/Linux/Linux…Онлайн-чтение исходного кода Linux

GitHub.com/Дай Цинлянь…Исходный код JDK онлайн-чтение