История изменений
Исправление firkax, (текущая версия) :
Вот в этом и проблема - делается куча ненужных захватов общего мютекса. И чем больше потоков (ядер) - тем больше доля таких бесполезных захватов.
Тут несколько проблем. Во-первых, чтобы сканировать мютексы файловых слотов, совсем не обязательно захватывать какой-то ещё один глобальный мютекс. Хотя у тебя он в качестве костыля от перерасхода проца используется.
Во-вторых, тут очевидная задача: поток хочет дождаться свободного слота. Не нужно ничего сканировать если слот не мог появиться. То есть ожидание слота должно быть какой-то атомарной операцией (кстати, а открытые файлы между тредами могут пересекаться? судя по тому, что ты писал до этого, не могут, и я буду придерживаться этой версии).
Чтобы не сканировать зря всё каждый раз, нужен счётчик свободных слотов. Если в нём ноль - сканировать ничего не надо. Для его увеличения/уменьшения только надо использовать atomic-инкременты и atomic-декременты, чтобы два потока, записывая в него одновременно, ничего не испортили. Если в нём не ноль - надо просканировать свободные слоты, и если слот нашёлся (он может и не найтись,если кто-то уже занял) занимаем его и декрементируем счётчик. Это уже работало бы лучше чем вариант с глобальным мютексом (то есть от первого недостатка мы избавились), но всё-таки второй недостаток остаётся: ожидание слота не атомарно, даже при ненулевом счётчике есть заметный шанс что ничего не получится и придётся опять как-то чего-то ждать.
Поэтому надо использовать не просто счётчик, а семафор. Семафор - это такой счётчик, у которого можно средствами операционной системы ждать ненулевое состояние и сразу забирать из него единицу как дождались, потокобезопасно. То есть создаём семафор, записываем в него количество свободных слотов. Когда нужно открыть файл - «ждём» его, как дождались - сканируем слоты и забираем свободный (он гарантированно будет, т.к. мы забрали единицу из семафора и никто другой в скан слотов не попадёт). Когда слот больше не нужен - освобождаем сначала слот, затем «освобождаем» (инкрементируем) специальной функцией семафор.
В итоге никаких постоянных сканирований и экспериментально подобранных sleep-ов, потоки просто ждут своей очереди, очередью руководит ядро ОС, эффективным образом.
На самом деле можно сделать ещё лучше и избавиться от сканирования вообще, даже от того, которое делается при подборе уже гарантированного слота. Но для этого нужно городить дополнительную логику и где-то поддерживать очередь свободных слотов со своим мютексом, для всего 30 слотов это, возможно, и нецелесообразно. А вот если слотов тысячи то уже точно нужно было бы.
Исправление firkax, :
Вот в этом и проблема - делается куча ненужных захватов общего мютекса. И чем больше потоков (ядер) - тем больше доля таких бесполезных захватов.
Тут несколько проблем. Во-первых, чтобы сканировать мютексы файловых слотов, совсем не обязательно захватывать какой-то ещё один глобальный мютекс. Хотя у тебя он в качестве костыля от перерасхода проца используется.
Во-вторых, тут очевидная задача: поток хочет дождаться свободного слота. Не нужно ничего сканировать если слот не мог появиться. То есть ожидание слота должно быть какой-то атомарной операцией (кстати, а открытые файлы между тредами могут пересекаться? судя по тому, что ты писал до этого, не могут, и я буду придерживаться этой версии).
Чтобы не сканировать зря всё каждый раз, нужен счётчик свободных слотов. Если в нём ноль - сканировать ничего не надо. Для его увеличения/уменьшения только надо использовать atomic-инкременты и atomic-декременты, чтобы два потока, записывая в него одновременно, ничего не испортили. Если в нём не ноль - надо просканировать свободные слоты, и если слот нашёлся (он может и не найтись,если кто-то уже занял) занимаем его и декрементируем счётчик. Это уже работало бы лучше чем вариант с глобальным мютексом (то есть от первого недостатка мы избавились), но всё-таки второй недостаток остаётся: ожидание слота не атомарно, даже при ненулевом счётчике есть заметный шанс что ничего не получится и придётся опять как-то чего-то ждать.
Поэтому надо использовать не просто счётчик, а семафор. Семафор - это такой счётчик, у которого можно средствами операционной системы ждать ненулевое состояние и сразу забирать из него единицу как дождались, потокобезопасно. То есть создаём семафор, записываем в него количество свободных слотов. Когда нужно открыть файл - «ждём» его, как дождались - сканируем слоты и забираем свободный (он гарантированно будет, т.к. мы забрали единицу из семафора и никто другой в скан слотов не попадёт). Когда слот больше не нужен - освобождаем сначала слот, затем «освобождаем» (инкрементируем) специальной функцией семафор.
В итоге никаких постоянных сканирований и экспериментально подобранных sleep-ов, потоки просто ждут своей очереди, очередью руководит ядро ОС, эффективным образом.
На самом деле можно сделать ещё лучшеи избавиться от сканирования вообще, даже от того, которое делается при подборе уже гарантированного слота. Но для этого нужно городить дополнительную логику и где-то поддерживать очередь свободных слотов со своим мютексом, для всего 30 слотов это, возможно, и нецелесообразно. А вот если слотов тысячи то уже точно нужно было бы.
Исходная версия firkax, :
Вот в этом и проблема - делается куча ненужных захватов общего мютекса. И чем больше потоков (ядер) - тем больше доля таких бесполезных захватов.
Тут несколько проблем. Во-первых, чтобы сканировать мютексы файловых слотов, совсем не обязательно захватывать какой-то ещё один глобальный мютекс. Хотя у тебя он в качестве костыля от перерасхода проца используется.
Во-вторых, тут очевидная задача: поток хочет дождаться свободного слота. Не нужно ничего сканировать если слот не мог появиться. То есть ожидание слота должно быть какой-то атомарной операцией (кстати, а открытые файлы между тредами могут пересекаться? судя по тому, что ты писал до этого, не могут, и я буду придерживаться этой версии).
Чтобы не сканировать зря всё каждый раз, нужен счётчик свободных слотов. Если в нём ноль - сканировать ничего не надо. Для его увеличения/уменьшения только надо использовать atomic-инкременты и atomic-декременты, чтобы два потока, записывая в него одновременно, ничего не испортили. Если в нём не ноль - надо просканировать свободные слоты, и если слот нашёлся (он может и не найтись,если кто-то уже занял) занимаем его и декрементируем счётчик. Это уже работало бы лучше чем вариант с глобальным мютексом (то есть от первого недостатка мы избавились), но всё-таки второй недостаток остаётся: ожидание слота не атомарно, даже при ненулевом счётчике есть заметный шанс что ничего не получится и придётся опять как-то чего-то ждать.
Поэтому надо использовать не просто счётчик, а семафор. Семафор - это такой счётчик, у которого можно средствами операционной системы ждать ненулевое состояние и сразу забирать из него единицу как дождались, потокобезопасно. То есть создаём семафор, записываем в него количество свободных слотов. Когда нужно открыть файл - «ждём» его, как дождались - сканируем слоты и забираем свободный (он гарантированно будет, т.к. мы забрали единицу из семафора и никто другой в скан слотов не попадёт). Когда слот больше не нужен - освобождаем сначала слот, затем «освобождаем» (инкрементируем) специальной функцией семафор.
В итоге никаких постоянных сканирований и экспериментально подобранных sleep-ов, потоки просто ждут своей очереди, очередью руководит ядро ОС, эффективным образом.