Преобразование комментариев в ответ.
Возможно, вам не хватает файловых дескрипторов.При параллельности 100 итераций цикла, которые создают 4 файловых дескриптора на каждую итерацию, могут привести к проблемам, если ограничение составляет около 256 дескрипторов.Да, вы закрываете некоторые из них быстро, но достаточно быстро?Это не ясно.А неопределенность планирования легко может объяснить изменяющееся поведение.
Насколько я понимаю, openmp состоит в том, что он входит в тело цикла n раз в то время, когда n - количество потоков (я ошибаюсь?).Поэтому в любой момент времени у меня никогда не должно быть более n * 2 файловых дескрипторов, которые на моем компьютере должны быть около 24.
Вероятно, n * 4 файловых дескриптора, но могут быть ограничения на параллелизм,Я недостаточно знаком с OpenMP, чтобы авторитетно комментировать это.Есть ли прагмы, кроме цикла for, который должен быть установлен?Мне не ясно, что запуск показанного кода привел к параллелизму на Mac, когда код скомпилирован с Clang - который не жалуется на #pragma
, в отличие от GCC 9.1.0, который предупреждает о неизвестной прагме под моимпараметры компиляции по умолчанию.
Однако, с форками и execs, а также потоками, жизнь становится хитрой.Файловые дескрипторы могут не закрываться, поэтому их следует закрывать, потому что файловые дескрипторы являются ресурсом уровня процесса, поэтому поток 1 может создавать файловые дескрипторы, о которых поток 2 не знает, но которые он разделяет.И затем, когда поток 2 разветвляется, дескрипторы файлов, созданные потоком 1, не закрываются, что не позволяет cat
правильно определить EOF и т. Д.
Один из способов убедиться в этом - использовать такую функцию, как эта:
#include <sys/stat.h>
static void dump_descriptors(int max_fd)
{
struct stat sb;
for (int fd = 0; fd <= max_fd; fd++)
putchar((fstat(fd, &sb) == 0) ? 'o' : '-');
putchar('\n');
fflush(stdout);
}
и в дочернем коде назовите его подходящим номером (возможно, 64 - может быть случай использования числа, равного 404).Хотя заманчиво попытаться использовать flockfile(stdout)
и funlockfile(stdout)
в функции, бессмысленно, если он вызывается только в дочернем процессе, потому что дочерний процесс однопоточный и, следовательно, не будет никакого вмешательства со стороны других потоков впроцесс.Однако слишком возможно, чтобы разные процессы могли мешать выводу друг друга.
Если вы собираетесь использовать dump_descriptor()
из потоков родительского процесса, то добавьте flockfile(stdout);
перед циклом и funlockfile(stdout);
после fflush()
звонка.Я не уверен, насколько это будет мешать проблеме;он обеспечивает однопоточное выполнение этой функции, потому что ни один из других потоков не может записывать в stdout
, пока один поток заблокирован.
Однако, когда я тестировал его с немного измененной версией кода, который выводит PIDдо «хороших» и «плохих» строк и до вывода dump_descriptors()
я никогда не видел чередования операций.Я получил вывод вроде:
14128: ooooooo----------------------------------------------------------
14128: good
14129: ooooooo----------------------------------------------------------
14129: good
14130: ooooooo----------------------------------------------------------
14130: good
…
14225: ooooooo----------------------------------------------------------
14225: good
14226: ooooooo----------------------------------------------------------
14226: good
14227: ooooooo----------------------------------------------------------
14227: good
, который настоятельно указывает на отсутствие параллелизма в коде.И когда нет параллелизма, вы не увидите проблемы.Каждый раз есть 4 дескриптора для каналов, и код тщательно закрывает их.
Подумайте о перенаправлении карты дескрипторов в файл (или один файл на каждого ребенка) в вашем сценарии, где вы, возможно, становитесь серьезнымипараллелизм.
Обратите внимание, что смешивание нитей с fork()
по своей сути сложно (как Джон Боллинджер отметил ) - обычно вы используете один или другой механизм, а не оба.