В настоящее время я работаю над генетическим алгоритмом, в котором новая популяция вычисляется с различными независимыми потоками.Моя программа отлично работает на OSX, но некоторые потоки не заканчиваются на машине с Linux.
У меня есть следующий метод, который выполняется каждым потоком, пока у меня не будет сгенерировано достаточно людей.Я использую один мьютекс, который является частью класса населения (как этот метод).Поэтому объект мьютекса не является статичным.Метод получает набор из parents
, из которых он может выбрать одного из родителей parents_max
первого, чтобы создать нового ребенка.Ребенок генерируется мутацией или рекомбинацией другого родительского объекта.Внутри select_randomly
и bool_with_prob
я использую несколько дистрибутивов из <random>
(все локальные переменные), чтобы выбрать случайного родителя или предоставить мне случайный бул.
Я искал в интернете причины, по которым темы победилине теряю и добавляю несколько (возможно ненужных) блокировок вокруг метода, который использует std::random_device
или std::mt19937
объекты.
void generate_childs(std::set<individual> &parents, double mutation_rate, size_t parents_max)
{
size_t individuals_size;
{
boost::lock_guard<boost::mutex> lock(mutex);
individuals_size = individuals.size();
}
auto selectable_parents_end = parents.begin();
std::advance(selectable_parents_end, parents_max);
while(individuals_size < size)
{
mutex.lock();
individual male = *utilities::container::select_randomly(parents.begin(), selectable_parents_end);
bool generate_child = utilities::container::bool_with_prob(0.3);
mutex.unlock();
boost::optional<individual> ind;
if(generate_child)
{
mutex.lock();
individual female = *utilities::container::select_randomly(parents.begin(), parents.end());
mutex.unlock();
ind = mutation::combined_mutated_child(male, female, mutation_rate);
} else
{
ind = mutation::mutated_child(male, 0.9);
}
if(ind && ind->is_valid())
{
boost::lock_guard<boost::mutex> lock(mutex);
if (individuals.size() < size) {
individuals.insert(*ind);
}
}
{
boost::lock_guard<boost::mutex> lock(mutex);
individuals_size = individuals.size();
}
}
}
Я запускаю потоки так:
unsigned int number_of_threads = std::thread::hardware_concurrency();
auto parents = individuals;
std::vector<boost::thread> threads;
for(size_t i = 0; i<number_of_threads; i++)
{
threads.emplace_back(&population::generate_childs,
this,
std::ref(parents),
mutation_rate,
parents_max);
}
for(auto &t: threads)
{
t.join();
std::cout << "Thread finished" << individuals.size() << std::endl;
}
При выполнении моей программы на локальной (OSX) машине, скомпилированной с Clang, она работает как положено.На моей машине с Linux это не заканчивается.Я даже пытался установить number_of_threads=1
, что не помогло.Когда программа не заканчивается на моем компьютере с Linux, я не могу выйти из нее с помощью Ctrl+C
.Любые идеи, где я мог бы иметь состояние гонки или тупик?
РЕДАКТИРОВАТЬ
Как предложено, я распечатал комментарий с идентификатором потока для каждого потока.Видимо, мое использование блокировки при обновлении размера было неуместным.Поэтому я изменил последнюю блокировку следующим образом:
std::cout << i << " updating size" << std::endl;
{
std::cout << i << " updating size about to lock" << std::endl;
boost::lock_guard<boost::mutex> lock(configuration::mutex);
std::cout << i << " updating size about to locked" << std::endl;
individuals_size = individuals.size();
if(individuals_size >= size)
{
std::cout << i << " returning" << std::endl;
return;
}
}
Вывод моей программы такой (пропущена часть, где потоки работали нормально):
0 started
2 started
3 started
3 entered while
0 entered while
1 started
2 entered while
1 entered while
3 got male 1
0 got male 0
3 got female
2 got male 1
1 got male 1
2 got female
1 got female
0 got mutated
0 before is valid
0 inserting
0 inserted
0 updating size
0 updating size about to lock
0 updating size about to locked
0 returning
Thread finished10
2 got combined
2 before is valid
2 inserting
2 inserted
2 updating size
2 updating size about to lock
2 updating size about to locked
2 returning
После этого я нене получить никаких дополнительных выходов.Мне кажется, что охранник не освобождает мьютекс.Это порядок, в котором я присоединяюсь к темам?Потому что я пытаюсь присоединиться к потоку 1 до 2, даже если он еще не закончен?