SFML community forums

Help => System => Topic started by: vechestva on December 07, 2012, 01:58:46 pm

Title: multithreading and srand
Post by: vechestva on December 07, 2012, 01:58:46 pm
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)
Title: Re: multithreading and srand
Post by: gyscos on December 07, 2012, 02:15:12 pm
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 :)
Title: Re: multithreading and srand
Post by: binary1248 on December 07, 2012, 04:44:17 pm
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.
Title: Re: multithreading and srand
Post by: vechestva on December 08, 2012, 04:32:04 am
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).
Title: Re: multithreading and srand
Post by: FRex on December 08, 2012, 04:45:33 am
The closest equivalent - Boost.Random? It's headers only + 'binary component for random_device'.
Title: Re: multithreading and srand
Post by: vechestva on December 08, 2012, 05:22:36 am
Boost
:(
Title: Re: multithreading and srand
Post by: cire on December 08, 2012, 06:33:06 am
http://www.johndcook.com/cpp_TR1_random.html
Title: Re: multithreading and srand
Post by: vechestva on December 08, 2012, 10:26:12 am
http://www.johndcook.com/cpp_TR1_random.html
I have visual studio 2008. There is no support <random> and rand_r (drand48_r).
Title: Re: multithreading and srand
Post by: Nexus on December 08, 2012, 10:42:27 am
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.
Title: Re: multithreading and srand
Post by: cire on December 08, 2012, 10:54:00 am
Quote
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.
Title: Re: multithreading and srand
Post by: vechestva on December 08, 2012, 01:50:46 pm
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.
Title: Re: multithreading and srand
Post by: Laurent on December 08, 2012, 01:55:42 pm
You didn't show us how you tried to use the mutex, and didn't say how it failed.
Title: Re: multithreading and srand
Post by: Nexus on December 08, 2012, 02:40:21 pm
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 :)
Title: Re: multithreading and srand
Post by: vechestva on December 09, 2012, 02:13:20 pm
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();
}
Title: Re: multithreading and srand
Post by: Laurent on December 09, 2012, 02:30:37 pm
And what's the problem with this code?
Title: Re: multithreading and srand
Post by: binary1248 on December 09, 2012, 04:34:04 pm
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.
Title: Re: multithreading and srand
Post by: vechestva on December 10, 2012, 08:44:55 am
binary1248, thank you. Thanks to all.