Это мой второй пост. Мой первый пост был в качестве старшекурсника от вступления до класса C. Я надеюсь, что в этот раз мне будет лучше.
Я разрабатываю стохастическую модель для своих исследований и ищу способы повышения эффективности вычислений в моих алгоритмах. Недавно коллега познакомил меня с многопоточностью, и я успешно реализовал ее в нескольких моих алгоритмах, не требующих блокировок ...
Теперь я пытаюсь изменить / проделать алгоритм, который потребует блокировок. В своей литературе / поисках StackOverflow / Google я довольно много узнал о классе мьютекса. Однако проблема, с которой я сталкиваюсь, кажется уникальной. Для контекста самое близкое, что я мог найти к ответу, было здесь:
Что происходит, когда два потока пытаются заблокировать один и тот же ресурс в одно и то же время?
По-прежнему не было прямого ответа на вопрос, который я собираюсь задать.
Я пытался контролировать доступ к памяти между потоками, используя динамически распределенный массив указателей на объекты мьютекса. Мой код компилируется без предупреждений / ошибок (скомпилировано с флагами -pthread -std = c ++ 11) и прекрасно работает вплоть до этого многопоточного алгоритма, в котором генерируется Segfault. После исследования с помощью lldb я обнаружил, что выбрасываемое исключение выглядит следующим образом:
Скриншот вывода lldb
После дальнейшего изучения я обнаружил, что все потоки, на которые ссылается вывод lldb, пытаются try_lock () для объекта мьютекса EXACT SAME, поскольку массив указателей, содержащий адрес этого объекта, был передан каждому потоку.
Мой вопрос, который похож на ранее упомянутый пост:
Что происходит, когда попытка try_lock () выполняется несколькими потоками (более одного) на одном и том же мьютексе в ТОЧНОЕ ЖЕ время (один и тот же тактовый такт процессора)? Есть ли у новых реализаций класса mutex обходные пути для этого, казалось бы, катастрофического события (т.е. shard_mutex, timed_mutex и т. Д.)?
Это недавно потрясло мой разум, так что любая проницательность будет принята с благодарностью. Большой привет сообществу S / O за всю помощь, на этом посте и на всех других, которые были неоценимы для моего роста как программиста.
ССЫЛКА НА КОД:
https://github.com/tylerbalbright/StackOverflow_7_4.git
Ошибка в файле RVEdata.cpp в строке 751 или 857.
ИСПРАВЛЕНО, НО НЕ РЕШЕНО:
Мне удалось исправить мой код, используя деку объектов мьютекса, в отличие от создания вектора указателей на динамически создаваемые мьютексы. Решение было предложено другими пользователями здесь:
Как я могу использовать что-то вроде std :: vector ?
В моем первом (неудачном) испытании я пытался создать массив указателей на мьютексы, например:
long int N = RuntimeVector.size(); //Varying size at runtime
std::mutex *MutexPtrs;
MutexPtrs = new std::mutex[N];
Затем я передал бы вновь созданный массив в функцию в качестве указателя, который передал бы указатель на массив в новый поток, например так:
void SomeFunction(std::mutex *PosLocks[])
{
.
..
...
SearchersPos.push_back(std::async(std::launch::async, &RVEdata::PositiveSearch, this, PosLocks));
}
Используя этот метод, код не завершался ошибкой при КАЖДОМ выполнении, но он завершался с ошибкой примерно в 90% случаев с EXC_BAD_ACCESS. Что интересно, так это то, что при КАЖДОМ неудачном выполнении был получен неправильный доступ, когда несколько потоков пытались попытаться заблокировать один и тот же мьютекс. Это НИКОГДА не завершалось неудачей, когда только один поток пытался выполнить try_lock на одиночном мьютексе. Когда я запускал тот же код на нашем HPC, сбои происходили примерно в 95% случаев, но я не мог найти столько информации в gdb, сколько мог бы в lldb (я менее опытен в командной строке gdb).
PS - я работаю на macOS High Sierra 10.13.6, Apple LLVM версии 10.0.0 (clang-1000.11.45.5).