简体   繁体   中英

C++ Initialization of static variable in multithreaded environment

I am struggling with a proper way to initialize static members of a class in C++. The context is random number generation. What I want to do is to create a class for random number generation and to create one engine per thread.

This looks something like this for now:

template<typename T, int MIN, int MAX>
class Rng {
 public:
  T operator()() {
    return get_random_number(omp_get_thread_num());
  }

 private:
  static T get_random_number(int id);
};

template<typename T, int MIN, int MAX>
T msl::Rng<T, MIN, MAX>::get_random_number(int id) {
  static std::vector<std::mt19937> engines;
  static std::vector<std::uniform_real_distribution<T>> dists;

  if (engines.empty()) { 
    int threads = omp_num_threads();
    engines.reserve(threads);
    dists.reserve(threads);

    for (int i = 0; i < threads; ++i) {
      std::random_device rd;
      std::mt19937 engine(rd());
      engines.push_back(engine);
      std::uniform_real_distribution<T> unif(MIN, MAX);
      dists.push_back(unif);
    }
  }

  return dists[id](engines[id]);
}

and I use it like this:

int main(){

Rng<double, 0, 10> rng{};
auto init = rng();

int n = 100;
int a[n];

#pragma omp parallel for
  for(int i = 0; i < n; ++i){
    a[i] = rng();
  }
}

Now I would like to get rid of the upfront initialization in the main method (auto init = rng()), but I don't want to introduce unnecessary overhead, ie threads should not wait for each other etc., since at the moment the initialization is done only once (obviously), but the generation happens quite a lot of times.

One thing I tried was to add a while loop so that only in the first run all threads wait for the master to initialize the engines:

template<typename T, int MIN, int MAX>
T msl::Rng<T, MIN, MAX>::get_random_number(int id) {
  static std::vector<std::mt19937> engines;
  static std::vector<std::uniform_real_distribution<T>> dists;

  int threads = omp_num_threads();

#pragma omp master
{
  if (engines.empty()) { 
    engines.reserve(threads);
    dists.reserve(threads);

    for (int i = 0; i < threads; ++i) {
      // init stuff...
    }
  }
}

  while(engines.size() < threads)
    continue; 

  return dists[id](engines[id]);
}

However, this just leads to a deadlock. What would be a typical way to solve this? I also thought about a mutex and conditional variable to wake up threads, but that is probably to costly because of the locking, which is actually only required the first time the function is called.

The typical way is to use a constructor .

Have the constructor of Rng perform these sorts of initialisations.

That is literally its job.

It's not clear why you have static data here in the first place, anyway.
Why not use member variables ?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM