【问题标题】:Confusion in Multithreading in C++C++中多线程的困惑
【发布时间】:2019-02-01 21:34:01
【问题描述】:

我试图模拟一个概率问题,其中有 n 个客户端和 n 个服务器。每个客户端随机向任何服务器发送请求,因此每个服务器可以接收任意数量的请求,我必须计算任何服务器可以接收的预期最大请求数。

我试图通过运行 10,000 次迭代来模拟这一点,在每次迭代中,每个客户端都会选择一个随机服务器并向其发送请求,服务器表示为大小为 N 的整数数组。

客户端选择一个随机数,然后服务器数组中该索引处的值递增。 因为,为了获得更好的结果,问题说 N 应该是大约 106

所以为了让它更快一点,我使用了多线程,其中每个线程运行 100 次迭代,总共有 10 个线程。

但是多线程代码产生的结果与普通代码截然不同。 下面是代码 sn-ps 以及它们的输出

普通版

 #include <iostream>
 #include <random>
 #include <chrono>

 #define N 1000000
 #define iterations 1000

int servers[N];

// This array's i'th index will contain count of in how many
// iterations was i the maximum number of requests received by any  server
int distr[N+1]={0};

int main(int argc, char const *argv[])
{   
   // Initialising
   auto start = std::chrono::high_resolution_clock::now();

   std::srand(time(NULL));

   // Performing iterations
   for(int itr=1; itr<=iterations; itr++)
   {
       for(int i=0;i<N;i++)
       {
           servers[i]=0;
       }

       for(int i=1;i<=N;i++)
       {
           int index = std::rand()%N;
           servers[index]++;
       }

       int maxRes = -1;
       for(int i=0;i<N;i++)
       {
           maxRes = std::max(maxRes, servers[i]);
       }
       distr[maxRes]+=1;
   }

   for(int i=0;i<=15;i++)
   {
      std::cout<<(double)distr[i]<<std::endl;
   }

   auto stop = std::chrono::high_resolution_clock::now();
   auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
   std::cout<<duration.count()<<" milliseconds"<<std::endl;

   return 0;
}

输出

0
0
0
0
0
0
0
359
552
79
10
0
0
0
0
0
1730 milliseconds

多线程版本

#include <iostream>
#include <random>
#include <chrono>
#include <thread>
#include <fstream>

#define N 100000
#define iterations 1000
#define threads 10

// This array's i'th index will contain count of in how many
// iterations was i the maximum number of requests received by any server
std::atomic<int> distr[N] = {};

void execute(int number)
{
    // Performing iterations
    int servers[N]={0};
    for(int itr=1; itr<=number; itr++)
    {

        for(int i=1;i<=N;i++)
        {
            int index = std::rand()%N;
            servers[index]++;
        }

        int maxRes = -1;
        for(int i=0;i<N;i++)
        {
            maxRes = std::max(maxRes, servers[i]);
            servers[i]=0;
        }

        distr[maxRes] += 1;
    }
}

int main(int argc, char const *argv[])
{   
    // Initialising
    auto start = std::chrono::high_resolution_clock::now();

    std::srand(time(NULL));

    std::thread t[threads];
    for(int i=0;i<threads;i++)
    {
        t[i] = std::thread(execute, iterations/threads);
    }   

    for(int i=0;i<threads;i++)
    {
        t[i].join();
    }

    for(int i=0;i<=15;i++)
    {
        double temp = (double)distr[i];
        std::cout<<i<<"\t"<<distr[i]<<std::endl;
    }

    auto stop = std::chrono::high_resolution_clock::now();

    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
    std::cout<<duration.count()<<" milliseconds"<<std::endl;

    return 0;
}

输出

0   0
1   0
2   0
3   0
4   0
5   0
6   0
7   7
8   201
9   421
10  267
11  68
12  2
13  2
14  4
15  0

1385 milliseconds

虽然我已经多次运行正常代码并且每次计数最大值 = 9 > 500,并且没有太多的数据分散,我的意思是只有最大值 = 8、9、10、11 具有重要的值其余都是零。

谁能解释一下我做错了什么?

提前致谢!

【问题讨论】:

  • 你在使用互斥锁吗?
  • 如果您使用 C++ 编程,请不要添加其他语言标签。
  • @MohammadrezaPanahi 但是哪一部分会有数据竞争?对于 distr 数组,我已经在使用 std::atomic ?
  • 也许您尝试解决的问题并没有您想象的那么可并行化?如果将线程数减半或加倍,结果会相差很大吗?哦,你的 CPU 有多少个内核?多于核心的线程可能会使其变慢。
  • 哦,顺便说一句,如果std::rand 是线程安全的,它的实现就被定义了。最好不要使用它,而是使用other standard PRNG facilities(例如std::uniform_int_distribution)。

标签: c++ multithreading random data-race


【解决方案1】:

我没有看到“非常不同的结果”,它们只是有些不同,所以看起来有点微妙。我注意到您没有单独为每个线程播种 - 这可能与它有关。

PS:如果你想要一个统一的分布,你不应该使用rand() % N。为什么?请参阅 Stephen Lavaveij 的this explanation。正如评论者所建议的那样,当N 很小但仍然存在时,偏差可能很小。

【讨论】:

  • 如果你想要一个统一的分布,永远不要使用 rand() % N! 这更像是一个评论而不是一个答案。如果N 远小于RAND_MAX,则由于RAND_MAX % N 不为零而导致的任何偏差对于任何东西都可以忽略不计,除了实际密码学等用途。能够成功编写此类代码的人已经知道不这样做。 您没有单独为每个线程播种 为什么?多次播种std::rand() 可能是错误的。
  • @hellow:已修复。
  • @AndrewHenle:你是对的,但是虽然N 在 OP 的玩具示例中可能很慢,但它可能会改变,对于阅读此问题的其他人来说可能并不低。
猜你喜欢
  • 2013-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多