Jump to content
  • Advertisement
Sign in to follow this  
TheComet

Threading question

This topic is 1474 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to learn more about multithreading and am a little confused.

 

Currently, I'm using Ogre3D and I'm processing my game entities asynchronously. This means each entity is writing to Ogre::SceneNode asynchronously whenever its position changes:

void processMyGameEntity(Entity& e)
{
    Vector3 newPosition = doFancyMovementCode(e);
    e.sceneNode->setPosition(newPosition);
}

// ... elsewhere
void Game::updateGame()
{
    for(auto& entity : m_EntityList)
        this->threadPool.push(processMyGameEntity(entity));
}

I know Ogre3D is using boost.thread in the background for rendering, so it is asynchronously reading from scene nodes, but I'm not synchronising when writing to the scenenodes. Why is this working fine? Is it safe for me to continue doing this?

 

Another question I have: The thread pool I'm using uses std::thread, but Ogre3D is using boost::thread. How do I synchronise objects being shared between the two?

Edited by TheComet

Share this post


Link to post
Share on other sites
Advertisement

I see. I also understand why those windows never overlap, as you put it, because Game::updateGame() is called when Ogre3D fires its frameRenderingQueued() event, which is right after Ogre has shovelled all of the data to the GPU and has nothing else to do. This works fine if there aren't many objects to draw, but if I understand this correctly, some synchronisation problems could occur if the rendering frame rate ever drops to the rate of game loop updates.

 

How do I mutually exclude e.sceneNode?

Share this post


Link to post
Share on other sites

Simply put, having one thread read from data at the same time as another is writing from it without synchronization is undefined behavior. The bad kind that does what you expect 99% of the time, and leaves impossible to trace errors the last 1%.

 

The simplest way to synchronize a large chunck of data is to a acquire a mutex when ever accessing that chunk of shared data. You always want to hold the mutex for as short an interval as possible.

Edited by King Mir

Share this post


Link to post
Share on other sites

Are there any common patterns for handling synchronization in games? It feels like having to grab a mutex every time I want to modify some data will quickly turn my multi-threaded game engine into an effectively single-threaded one, as all other threads will be blocked. How do you balance the need for thread-safety with the desire to not block other threads from running?

Share this post


Link to post
Share on other sites

It feels like having to grab a mutex every time I want to modify some data will quickly turn my multi-threaded game engine into an effectively single-threaded one, as all other threads will be blocked.

Yep, therefor you should avoid this as much as possible. This is somewhat special about game-development, sometimes an application developer will rejoice when a process finishes after just below 200ms, whereas a game developer will break down once a process needs more then 2ms. And synchronisation is one of the fastes ways to surpass the 2ms wink.png

 

 


This works fine if there aren't many objects to draw, but if I understand this correctly, some synchronisation problems could occur if the rendering frame rate ever drops to the rate of game loop updates.

How do you balance the need for thread-safety

You dont need to balance thread-safety, because your code must be thread-safe period. It is just the matter how hard it is to ensure thread-safety. If you use mutex/semaphores/critical sections etc. it is quite easy, but potentially slow. If you use lock-less sync by contract, you are at a much higher risk of including bugs, but it is much faster.

 

On the other hand, maximum CPU usage is not always needed, and modern, powerful CPUs remove the burden of hobby/indie developer of squezzing out the last drop of performance. So, as long as you don't try to invent the next block-buster AAA engine, I would sugguest to use a good mix of manageable code and block-less concurrrency behavior, which feels comfortable. Just remember, the best, fastest code is worth nothing, if you can't handle it over a long period.

Edited by Ashaman73

Share this post


Link to post
Share on other sites


I know Ogre3D is using boost.thread in the background for rendering, so it is asynchronously reading from scene nodes, but I'm not synchronising when writing to the scenenodes. Why is this working fine? Is it safe for me to continue doing this?
 

 

Normally, if you put a bunch of jobs into a thread pool, you then wait for them to finish somewhere. If updateGame is the safe time to update game entities, you would wait for all those pooled updates to finish right after scheduling them, if your game has nothing else it can do before waiting.

 

However, as your game gets more complex, I'd expect "doFancyMovementCode" to need to read data from other entities. How are you planning on dealing with that?

Share this post


Link to post
Share on other sites


However, as your game gets more complex, I'd expect "doFancyMovementCode" to need to read data from other entities. How are you planning on dealing with that?

I've been reading about different types of locks (boost synchronization chapter), and I think the most efficient way to approach that problem is to use read/write locks (shared_lock). As I understand it, multiple threads are allowed to read the same data without having to lock. The lock is only acquired if a thread tries to write to data being read.

Share this post


Link to post
Share on other sites
Not quite. A reader/writer lock is still a lock. It is typically cheaper to take a read lock than a write lock, but it's still a lock.

Here's why:

- Thread A begins reading data from 0xf00
- Thread B locks 0xf00
- Thread B begins a series of writes to 0xf00
- Thread A resumes and sees partially written data
- Thread B unlocks
- Thread A is now in bogus state

If by contrast a read lock is treated like a lock:

- Thread A locks 0xf00 for read
- Thread B blocks waiting for write-lock on 0xf00
- Thread A completes its stuff and unlocks
- Thread B resumes and safely takes the write-lock and updates 0xf00
- Thread A cannot interrupt this because the read lock will block the thread until B unlocks

The main advantage to reader/writer locks is that you can cheaply read from many threads, but you still need to lock, so it isn't free. It's also much easier to introduce nasty bugs than with simple mutual exclusion. I generally recommend against R/W locks unless there is hard evidence that they can reduce threat contention, for that very reason.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!