C++ 使用多线程时给出不同答案的Openmp多线程代码
我有以下带有C++ 使用多线程时给出不同答案的Openmp多线程代码,c++,multithreading,openmp,C++,Multithreading,Openmp,我有以下带有openmp多线程的蒙特卡罗代码 int main() { std::uniform_int_distribution<int> dir(0, 1); std::uniform_int_distribution<int> xyz(0, 2); std::uniform_real_distribution<double> angle(0,360); mt19937 gen{0}; auto M = 20; long doub
openmp
多线程的蒙特卡罗代码
int main()
{
std::uniform_int_distribution<int> dir(0, 1);
std::uniform_int_distribution<int> xyz(0, 2);
std::uniform_real_distribution<double> angle(0,360);
mt19937 gen{0};
auto M = 20;
long double sum = 0;
auto num_trials = 10000;
// omp_set_num_threads(12);
#pragma omp parallel
{
double loc_sum = 0.0;
#pragma omp for
for(int i = 0; i < num_trials; ++i)
{
double x = 0;
double y = 0;
double z = 0;
double r = 0;
auto N = 0;
while(r < M)
{
auto d = dir(gen);
auto p = xyz(gen);
if(p == 0)
{
x += (d == 1) ? -1 : 1;
}
else if(p == 1)
{
y += (d == 1) ? -1 : 1;
}
else
{
z += (d == 1) ? -1 : 1;
}
r = std::sqrt(x * x + y * y + z * z);
++N;
}
loc_sum += N;
}
#pragma omp critical
sum += loc_sum;
}
}
intmain()
{
标准:统一分布目录(0,1);
均匀分布xyz(0,2);
标准:均匀实分布角(0360);
mt19937 gen{0};
自动M=20;
长双和=0;
自动试验次数=10000次;
//omp_设置_数量_线程(12);
#pragma-omp并行
{
双loc_和=0.0;
#pragma omp for
对于(int i=0;i
变量sum
对于串行线程和多线程执行是完全不同的。我希望由于对随机均匀分布的调用会有轻微的差异,但我观察到的差异太大,不能归因于随机性,我怀疑我的多线程代码中存在缺陷
此代码中是否存在影响
sum
的争用条件或数据争用?问题是您调用生成器(dir
和xyz
)时没有锁定它们。您也在使用PRNG(gen
)而不锁定
这两个调用都不是原子的,因为默认情况下实现它们会使单线程代码比需要的慢
用#pragma omp critical
标记要生成d
和p
的行可以解决此问题
如果不需要关键部分,则需要在每个线程中分别设置dir
、xyz
和gen
对象。可以简单地复制生成器(dir
和xyz
)。每个线程的PRNG(gen
)应该正确初始化,否则每个线程中的PNG状态都会完全相同。例如:
std::random_device rd; /* Outside the parallel section. */
// Code below once per thread.
/* Initialization of the PRNG calls std::random_device::operator() which
* needs a lock around it when called in parallel. */
std::mt199937 gen;
#pragma omp critical
{
gen.seed(rd());
}
// for-loop starts here.
我很确定如果没有锁,你不能调用这些生成器(
dir
和xyz
)。您也在使用PRNG(gen
)而不锁定。如果您用#pragma omp critical
标记生成d
和p
的行,代码是否有效?@Darhuuk啊,我认为您是对的。将d,p
包裹在一个关键部分,它可以工作。我原以为发电机会自动产生价值。实际上,我不想在d,p
周围放一个关键部分。这里最好的解决方案是在并行区域内创建生成器吗?是的,在这种情况下,最好的办法是创建生成器的副本。这些应该很轻。您可能不想复制PRNG对象,因为这样每个线程中的状态都完全相同。因此,我将在for循环中创建它。在正确的答案中扩展我的注释。在rd()
调用中是否存在竞争条件?除非明确提到所讨论的函数是原子函数,否则我将假设将存在竞争条件。标准库中的函数不太可能是原子函数,因为这会使它比不使其原子化慢。也就是说,通过使其原子化,您可以惩罚从单个线程使用该函数的所有人。相反,如果您将函数编写为非原子函数,那么无论如何都可以通过在调用周围加上一个锁来使它们成为原子函数。现在,您只需要在真正需要时支付锁定/原子调用的费用。此外,生成器种子设定的关键部分不应该对程序的运行时产生任何明显的影响,因为每个线程只运行一次。