Sign in to follow this  
dev578

Mutlithreading Critical Sections

Recommended Posts

I am writing a game server using mutiple threads, and there is quite a bit of shared data between the threads. Do I need to put something in a mutex if it is only reading? I know you need to put it in a mutex if it writes, but I keep going back and forth if you need to or not for reading. Example, do I need to do this: for (int i = 0; i < p->nNumEnemies; i++) { //do whatever } or WaitForSingleObject(p->hEnemiesMutex,INFINITE); int CopiedVariable = p->nNumEnemies; ReleseMutex(p->hEnemiesMutex); for (int i = 0; i < CopiedVariable; i++) { //do whatever } If the second of the two is neccessary, then I have a lot of work to do, there are many instances of that. Thank you Dev578

Share this post


Link to post
Share on other sites
The basic data problem in threading is that if you read (or write) data while modifying it, everything goes to hell. If you can guarantee that there are no modifications to the target data, you can read it without creating a critical section for the duration of the read.

But the moment you need to start writing, things get rather more complex, and you want to be very careful how you go about it. Remember, mutexes are kernel objects -- you should keep them to a reasonable minimum.

Share this post


Link to post
Share on other sites
Basically any data that is being accessed by the threads will need protected. You want to ensure that multiple threads can not write to the same piece of memory (as you have already stated). Also you will want to account for a thread writing, while another thread tries to read from the same memory (And vise versa).

For instance each thread is allocated a time slice to run on the CPU. Thread 1 might start reading the data and then its time slice expires. This allows thread 2 to write to the same memory Thread 1 was reading. Thread 2's time slice expires allowing Thread 1 to resume reading the data. Because we did not lock the memory, Thread 1 read in a unexpected corupted value.

Generally if the data is an constant value that will NEVER change, then it might be safe to allow multiple threads to access it without locking. However, I generally try to avoid this to be on the safe side.

Share this post


Link to post
Share on other sites
I can't imagine that your program is only reading the data.
Somewhere, someone, at some point, is actually writing it, ain't it so?

If it is, then yes, you should keep this mutex, to protect against read-write conflict.
If not, remove it, as concurrent reading is perfectly safe.


Another aspect, is that you might want to protect larger parts of code.
I see that you only care about nNumEnemies variable, and after that you start iterating through the container. The thing is, if someone is modifying the variable after you read it, he is also probably modifying the container itself (for example, removing an element) - while you're iterating! So the entire operation should be protected by one mutex.

Also, you might consider using CRITICAL_SECTION, as it's much more lightweight than mutex, and should be preferred in inter-thread usage, as opposed to inter-process synchronisation, which requirers usage of mutex.

HTH
~def

Share this post


Link to post
Share on other sites
The read/write situation is a common one in making robust multithreaded apps. What you need is a special kind of mutex which can be locked in a shared or unshared mode. If the lock is grabbed in unshared mode, everyone else blocks on it until the lock is released. If the lock is grabbed in shared mode, only threads which want to grab it in unshared mode block. (To prevent starvation, a thread which is blocking on grabbing in unshared mode will generally cause other threads which want to grab in shared mode to block too). These slides seem to present things pretty well.

Share this post


Link to post
Share on other sites
yeah, you can run into problems just reading... one scenario would be what if there's nothing to read or rather what if the buffer going to be read isn't ready to be read? in these cases you have to wait for something to read, which would require blocking to prevent reading invalid, corrupt, or possibly old and incorrect data. this is the producer-consumer problem, where the consumer reads what is produced... but the consumer can't read if the producer hasn't written anything yet... hope that makes sense lol.

Share this post


Link to post
Share on other sites
The first level answer is that if you have to ask, you should use sychronization even for reads. As somebody else mentioned, in Win32 a critical section will be much more efficient than a mutex. This is usually the safest path but if you're not careful you can actually end up with worse performance that a single threaded design and you need to worry about deadlocks.

The next level answer would be that you are safe as long as the reads are atomic. i.e. it's impossible for the data to change while you are reading it. An example would be if you had to read two ints - you might end up with the first int and the second being inconsistent because another thread was writing at the same time you were reading. You can also run into the problem of the data going away entirely while you're reading it.

The third level answer is that even if you get all the above stuff right then on some multiprocessor architectures you can still be screwed unless you use aquire/release. Things get really subtle at this point.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this