线程不是在Linux上终止而是在Mac上终止

时间:2019-05-29 22:36:10

标签: c++ linux multithreading boost

我目前正在研究一种遗传算法,其中使用不同的独立线程来计算新种群。我的程序在OSX上运行良好,但某些线程在Linux机器上未终止。

我有以下方法,每个线程都会执行此方法,直到生成足够的个人为止。我正在使用作为人口类别的一部分的单个互斥锁(因为此方法是)。因此,互斥对象不是静态的。该方法通过了一组parents的传递,它可以在第一个parents_max的父母中选择一个,以生成一个新的孩子。一个孩子是由另一个父母反对的突变或重组产生的。在select_randomlybool_with_prob内部,我使用<random>(所有局部变量)的多个分布来选择一个随机父代或为我提供一个随机布尔。

我在互联网上搜索了为什么线程不会终止的原因,并在使用std::random_devicestd::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;
}

在用Clang编译的本地(OSX)计算机上执行我的程序时,它会按预期执行。在我的Linux机器上,它没有完成。我什至尝试设置number_of_threads=1,但没有帮助。当程序没有在我的Linux机器上终止时,我无法使用Ctrl+C退出它。有什么想法可能会导致比赛状态或僵局吗?

编辑

根据建议,我为每个线程打印了带有线程ID的注释。显然,我在更新大小时使用锁是不合适的。因此,我修改了最后一个锁,如下所示:

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之前加入线程1,即使它尚未完成?

1 个答案:

答案 0 :(得分:1)

我在您的代码中不喜欢的一件事是您滥用锁。例如,当您获取容器的大小时,无法确定解锁互斥锁后刚获取的大小是否正确。因此,正确的模式可能是将代码块锁定在您检索大小并在假定该大小正确的情况下使用容器的情况下进行处理,并在不再需要此容器时将其解锁。

因此,您应该重新编写代码,因为在许多地方都可能存在竞争条件。您的问题的一种可能答案,请看下面的代码:

    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已过时。