Потоки не заканчиваются на Linux, но на Mac - PullRequest
0 голосов
/ 30 мая 2019

В настоящее время я работаю над генетическим алгоритмом, в котором новая популяция вычисляется с различными независимыми потоками.Моя программа отлично работает на 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, даже если он еще не закончен?

1 Ответ

1 голос
/ 30 мая 2019

Одна вещь, которая мне не нравится в вашем коде, это то, что вы неправильно используете блокировки. Например, когда вы получаете размер контейнера, вы не можете быть уверены, что размер, который вы только что получили, является правильным сразу после разблокирования мьютекса. Таким образом, правильный шаблон может заключаться в блокировке блока кода, в котором вы извлекаете размер, и работе с контейнером в предположении, что этот размер правильный, и разблокировании его, когда вам больше не нужен этот контейнер.

Так что вам следует переработать код, потому что во многих местах могут быть условия гонки. Один из возможных ответов на ваш вопрос, посмотрите на код ниже:

    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();

Какое исключение выдается в select_randomly? Вы никогда не разблокируете мьютекс, и это условие тупика. Почему это может вызвать исключение? Например, потому что значение selectable_parents_end устарело из-за состояния гонки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...