-
Hi. :)
How to use a randomizer with multithreading?
simple ex:
class Myc
{
public:
Myc()
{
}
~Myc()
{
for (int i = 0; i < threads.size(); ++i)
if(sf::Thread* thr = threads[i])
{
thr->terminate();
delete thr;
}
threads.clear();
}
void Start()
{
// 3 thread
for (int i = 0; i < 3; ++i)
threads.push_back(new sf::Thread(&Myc::ThrFun, this));
for (int i = 0; i < threads.size(); ++i)
threads[i]->launch();
}
void ThrFun()
{
sf::Clock clock;
while (true)
{
if (clock.getElapsedTime().asSeconds() >= 3)
{
// problem
int r = rand()%100+1;
std::cout << r << std::endl;
clock.restart();
}
}
}
private:
std::vector<sf::Thread*> threads;
};
int main()
{
srand((unsigned)time(NULL));
Myc m;
m.Start();
std::cin.get();
}
result:
The same value in each thread. Since the mutex does not work, or I did not use true.
(http://eovq.narod.ru/thread.gif)
-
rand() is not thread-safe.
http://linux.die.net/man/3/rand
Also, this doesn't really concern SFML, and is just a general C question.
Just google "rand thread safe" and you'll find plenty of information :)
-
Or you can use something more modern like my favourite:
#include <ctime>
#include <iostream>
#include <random>
int main () {
std::mt19937 generator( static_cast<unsigned int>( time( NULL ) ) );
std::uniform_int_distribution<int> distribution( 1, 100 );
std::cout << "Random value: " << distribution( generator ) << std::endl;
return 0;
}
You would have to create one generator and one distribution per thread but I think that is acceptable if you want to keep things simple. If you are getting the same values from all threads you might need to seed with something else than time( NULL ) because it returns the same time if you call it multiple times within the same second. If you have support for the new <chrono> library then you should use that instead.
-
I have visual studio 2008. There is no support <random> and rand_r (drand48_r). How to solve the problem? (change "IDE" is not an option).
-
The closest equivalent - Boost.Random? It's headers only + 'binary component for random_device'.
-
Boost
:(
-
http://www.johndcook.com/cpp_TR1_random.html
-
http://www.johndcook.com/cpp_TR1_random.html
I have visual studio 2008. There is no support <random> and rand_r (drand48_r).
-
In order to use TR1 on Visual Studio 2008, you have to install the Service Pack 1. Why don't you want to use Boost? It contains tons of useful features, not only random generation.
The alternative is to protect global rand() calls with a mutex. But this can quickly become slow. An own random engine per thread is much cleaner. And you can enforce deterministic generation of random numbers, which may be helpful for debugging.
-
I have visual studio 2008. There is no support <random> and rand_r (drand48_r).
All that says to me is that you didn't actually take the time to read the material in the link.
-
thx, but:
tons
This is the key word. ;)
The alternative is to protect global rand() calls with a mutex. But this can quickly become slow. An own random engine per thread is much cleaner. And you can enforce deterministic generation of random numbers, which may be helpful for debugging.
Mutex also fails. :'(
Since the mutex does not work, or I did not use true.
-
You didn't show us how you tried to use the mutex, and didn't say how it failed.
-
tons
This is the key word. ;)
You know that Boost is a collection of libraries, written by different authors? Designs differ sometimes strongly between them. Boost.Random isn't the kind of library which is massively overengineered, it is even part of the C++ standard library now.
Take a more detailed look at Boost and don't just believe what you've heard, I am sure there are parts which you find useful :)
-
You didn't show us how you tried to use the mutex, and didn't say how it failed.
class Myc
{
public:
Myc()
{
}
~Myc()
{
for (int i = 0; i < m_threads.size(); ++i)
if(sf::Thread* thr = m_threads[i])
{
thr->terminate();
delete thr;
}
m_threads.clear();
}
void Start()
{
// 3 thread
for (int i = 0; i < 3; ++i)
m_threads.push_back(new sf::Thread(&Myc::ThrFun, this));
for (int i = 0; i < m_threads.size(); ++i)
m_threads[i]->launch();
}
void ThrFun()
{
sf::Clock clock;
while (true)
{
if (clock.getElapsedTime().asSeconds() >= 3)
{
// problem
m_mutex.lock();
int r = rand()%100+1;
m_mutex.unlock();
std::cout << r << std::endl;
clock.restart();
}
}
}
private:
std::vector<sf::Thread*> m_threads;
sf::Mutex m_mutex;
};
int main()
{
srand((unsigned)time(NULL));
Myc m;
m.Start();
std::cin.get();
}
-
And what's the problem with this code?
-
Actually this discussion went in the totally wrong direction, mostly because it wasn't stated early enough that VS 2008 was being used. Being as POSIX compliant as they are MS went and made all the thread-unsafe functions in the C runtime thread-safe in their runtime libraries, which is also the reason they don't need to have rand_r.
In the MS runtime the rand() state is stored locally per thread, which means you can use rand() without mutexes, you just have to make sure to seed each thread with a different value (this means you shouldn't use time()).
#include <ctime>
#include <iostream>
#include <vector>
#include <SFML/System.hpp>
class Myc
{
public:
Myc()
{
if(!seed)
{
seed = time(NULL);
}
}
~Myc()
{
for (int i = 0; i < m_threads.size(); ++i)
if(sf::Thread* thr = m_threads[i])
{
thr->terminate();
delete thr;
}
m_threads.clear();
}
void Start()
{
// 3 thread
for (int i = 0; i < 3; ++i)
m_threads.push_back(new sf::Thread(&Myc::ThrFun, this));
for (int i = 0; i < m_threads.size(); ++i)
m_threads[i]->launch();
}
void ThrFun()
{
sf::Clock clock;
srand(seed++);
while (true)
{
if (clock.getElapsedTime().asSeconds() >= 3)
{
int r = rand()%100+1;
std::cout << r << std::endl;
clock.restart();
}
}
}
private:
static unsigned int seed;
std::vector<sf::Thread*> m_threads;
};
unsigned int Myc::seed = 0;
int main()
{
Myc m;
m.Start();
std::cin.get();
}
This should work, but only on Windows does it run safely.
-
binary1248, thank you. Thanks to all.