Boost和C++11线程兼容性

3

我有一段代码,在ubuntu系统下使用boost线程可以正常运行。它是基于只读数据共享的多线程操作。现在我尝试用C++11替代boost来实现非常基础的转换,虽然代码能够编译通过,但出现了一些难以排查的bug,导致C++11标准线程会随机崩溃。我尝试使用valgrind drd进行调试,但调试信息很难理解。请问有什么思路吗?

==19608== Conflicting load by thread 3 at 0x00643be8 size 8
==19608==    at 0x41CEBA: std::subtract_with_carry_engine<unsigned int, 24ul, 10ul, 24ul>::operator()() (random.tcc:601)
==19608==    by 0x41CD6C: double std::generate_canonical<double, 53ul, std::subtract_with_carry_engine<unsigned int, 24ul, 10ul, 24ul> >(std::subtract_with_carry_engine<unsigned int, 24ul, 10ul, 24ul>&) (random.tcc:3475)
==19608==    by 0x41CB06: std::__detail::_Adaptor<std::subtract_with_carry_engine<unsigned int, 24ul, 10ul, 24ul>, double>::operator()() (random.h:190)
==19608==    by 0x41C877: double std::normal_distribution<double>::operator()<std::subtract_with_carry_engine<unsigned int, 24ul, 10ul, 24ul> >(std::subtract_with_carry_engine<unsigned int, 24ul, 10ul, 24ul>&, std::normal_distribution<double>::param_type const&) (random.tcc:1950)
==19608==    by 0x41C688: double std::normal_distribution<double>::operator()<std::subtract_with_carry_engine<unsigned int, 24ul, 10ul, 24ul> >(std::subtract_with_carry_engine<unsigned int, 24ul, 10ul, 24ul>&) (random.h:2196)
==19608==    by 0x41C379: nrand(double, double) (NRand.cpp:8)
==19608==    by 0x416F78: ClassDef::set_x() (LoanDef.h:407)
==19608==    by 0x4168CA: Sim(std::vector<ClassDef, std::allocator<ClassDef> >&, Assumption&, std::unordered_map<std::string, std::vector<double, std::allocator<double> >, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, std::vector<double, std::allocator<double> > > > >&, unsigned int, NextStepCalcML&, std::vector<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >, std::allocator<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > > > >&, unsigned int, unsigned int) (NewSim.cpp:105)
==19608==    by 0x411A09: _ZNSt12_Bind_simpleIFPFvRSt6vectorI7LoanDefSaIS1_EER10AssumptionRSt13unordered_mapISsS0_IdSaIdEESt4hashISsESt8equal_toISsESaISt4pairIKSsS9_EEEjR14NextStepCalcMLRS0_IS0_IS9_SaIS9_EESaISN_EEjjESt17reference_wrapperIS3_EST_IS5_EST_ISI_EjST_ISK_EST_ISP_EjjEE9_M_invokeIILm0ELm1ELm2ELm3ELm4ELm5ELm6ELm7EEEEvSt12_Index_tupleIIXspT_EEE (functional:1732)
==19608==    by 0x4116AE: std::_Bind_simple<void (*()(std::reference_wrapper<std::vector<LoanDef, std::allocator<LoanDef> > >, std::reference_wrapper<Assumption>, std::reference_wrapper<std::unordered_map<std::string, std::vector<double, std::allocator<double> >, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, std::vector<double, std::allocator<double> > > > > >, unsigned int, std::reference_wrapper<NextStepCalcML>, std::reference_wrapper<std::vector<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >, std::allocator<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > > > > >, unsigned int, unsigned int))(std::vector<LoanDef, std::allocator<ClassDef> >&, Assumption&, std::unordered_map<std::string, std::vector<double, std::allocator<double> >, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, std::vector<double, std::allocator<double> > > > >&, unsigned int, NextStep&, std::vector<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >, std::allocator<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > > > >&, unsigned int, unsigned int)>::operator()() (functional:1720)
==19608==    by 0x411647: std::thread::_Impl<std::_Bind_simple<void (*()(std::reference_wrapper<std::vector<ClassDef, std::allocator<ClassDef> > >, std::reference_wrapper<Assumption>, std::reference_wrapper<std::unordered_map<std::string, std::vector<double, std::allocator<double> >, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, std::vector<double, std::allocator<double> > > > > >, unsigned int, std::reference_wrapper<NextStep>, std::reference_wrapper<std::vector<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >, std::allocator<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > > > > >, unsigned int, unsigned int))(std::vector<LoanDef, std::allocator<LoanDef> >&, Assumption&, std::unordered_map<std::string, std::vector<double, std::allocator<double> >, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, std::vector<double, std::allocator<double> > > > >&, unsigned int, NextStep&, std::vector<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >, std::allocator<std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > > > >&, unsigned int, unsigned int)> >::_M_run() (thread:115)

谢谢。顺便说一下,我尝试使用我编写的这个随机头文件,在多线程环境下是否安全还不确定。它在使用boost时曾经很好地工作过。
我的随机数头文件(myrand.hpp):
#ifndef NRAND_H
#define NRAND_H
#include <random>

double nrand(double mean = 0., double sd = 1.);
double urand(double a=0., double b=0.);

#endif

还有myrand.cpp

#include "NRand.h"
using namespace std;

double nrand(double mean, double sd) {
    static random_device rd;
    static subtract_with_carry_engine<unsigned,24,10,24> e(rd());
    normal_distribution<> dist(mean, sd);
    return dist(e);
}

double urand(double a, double b) {
    static random_device rd;
    static subtract_with_carry_engine<unsigned,24,10,24> e(rd());
    uniform_real_distribution<> dist(a, b);
    return dist(e);
}

many thanks.


@Rapptz 这个问题在这里无疑是有用的,但是没有答案。 - Walter
2
你的 nrandurand 没有任何线程安全性,并且会引发未定义的行为。 - Casey
我应该将随机设备和引擎设置为非静态吗?那么如何消除开销呢?谢谢。 - bbc
1
你可能需要记住的另一件事是,如果线程没有被加入或分离,std::thread 的析构函数会调用 std::terminate(相关链接:https://dev59.com/22855IYBdhLWcg3wJgxy)。 - MFH
1个回答

2

在我看来,你的代码不是线程安全的,因此在C++11下可能无法正常工作。我认为问题在于static变量rde是全局变量,但没有受到保护(通过互斥锁),因此并发调用将会竞争。

可以通过将这些变量设置为thread_local来使代码线程安全,但我没有经验。


谢谢,它可以使用thread_local。 - bbc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接