Sign in to follow this  
assainator

Critical section question

Recommended Posts

Hello everyone,

I have a function that ask for a reference to a constant std::vector.
Example:
[code]bool DoSomething(const std::vector<int>& data);[/code]
The thing is, I would like this function to be multi-threaded. I've learned, by reading tutorials, that any data that is not local, should be protected by for example a mutex. But the tutorials assumed that the data that needed protected would be modified by the multi-threaded function.

Now the question is, does any data that is constant or is guaranteed to not be modified need a kind of protection? I assume here that only one function will access the data, although this function will be run in multiple threads.

I thank you in advance.

Share this post


Link to post
Share on other sites
Just because DoSomething() accepts the vector via a reference-to-const doesn't mean the vector is actually constant. Even if it is const, you have to be careful with member functions that are logically const, but do actually mutate memory in some way. I doubt any implementation of std::vector would do anything like this (though using debug iterators *might* be fishy...) but look out for things with caches which have methods declared as const, but aren't const as far as memory mutation is concerned.

If you can guarantee (by inspection of related code) that the object and the memory used for its implementation aren't modified, then yes, multiple threads can read from it concurrently.

Share this post


Link to post
Share on other sites
If by "guaranteed to not be modified" you mean "guaranteed to not be modified [b][i]by this function[/i][/b]", the answer is that it still needs protection.

The reason is that [i][b]presumably [/b][/i]other threads may be changing the data, and it is theoretically possible for this function's [i][b]read [/b][/i]of the data to be corrupted by a concurrent write by another thread.

If your program is able to guarantee that [i][b]no [/b][/i]threads will be writing to the data during execution of this function, then you shouldn't need protection at this level. Simultaneous reads are safe on most architectures.

Share this post


Link to post
Share on other sites
As mentioned, being constant means your function cannot modify it, but another thread easily could be while your function is reading from it.

The typical way of handling this is to “lock” the resource your function wants to read (in this case a vector) and “unlock” it when it is done reading from it.
If you are worried that this could happen, you should subclass the vector and add some locking routines to it. After calling Lock(), the resource enters a read-only state which allows any number of threads to simultaneously read from it. Write operations are blocked until Unlock() has been called the same number of times as Lock().


L. Spiro

Share this post


Link to post
Share on other sites
[quote name='YogurtEmperor' timestamp='1317876658' post='4869659']
The typical way of handling this is to “lock” the resource your function wants to read (in this case a vector) and “unlock” it when it is done reading from it.
[/quote]
If data is going to be manipulated, it must be locked [i]everywhere[/i] it is accessed, not just where it is read.

[quote]
If you are worried that this could happen, you should subclass the vector and add some locking routines to it.
[/quote]
std::vector is not designed to be subclassed. If you did want to add locking at this granularity, it would be preferable to write a wrapper that locks and forwards. Or to design a system that leverages RAII to handle unlocking, e.g. scoped_lock.

[quote]
After calling Lock(), the resource enters a read-only state which allows any number of threads to simultaneously read from it. Write operations are blocked until Unlock() has been called the same number of times as Lock().
[/quote]
You would need to have an API that supports the distinction locking for reading and writing to support this. Otherwise you can only support mutual exclusion, you could not support multiple readers.

@OP The better solutions involving multi-threading synchronisation are usually a high level architectural choices, not low level locking. For example, if the vector isn't excessively big, it can be cheaper to have multiple copies rather than trying to enforce locking.

Share this post


Link to post
Share on other sites
I agree with everything you said, but I figured the original poster would be trying to avoid re-arranging his code to add high-level managers etc.

And yes making a Lock()/Unlock() system that allows multiple reads but not write is non-trivial. Which is why I thought about it for a while but was not able to provide a suggestion as to how he could do this.
I was hoping he would try a stack of critical section locks, find out it is mutually exclusive, and post back with a question on how to do this. Teehee.
I didn’t have time to dream up a solution with the Interlocked* functions (assuming Win32 API), as my limited experience with them would require me to study the whole set of functions to see which ones do what.


L. Spiro

Share this post


Link to post
Share on other sites
If you need a multiple reader single writer lock one option is a [url="http://www.boost.org/doc/libs/1_41_0/doc/html/thread/synchronization.html#thread.synchronization.mutex_types.shared_mutex"]boost shared_mutex[/url].

Share this post


Link to post
Share on other sites
[quote name='rip-off' timestamp='1317892048' post='4869713']
@OP The better solutions involving multi-threading synchronisation are usually a high level architectural choices, not low level locking. For example, if the vector isn't excessively big, it can be cheaper to have multiple copies rather than trying to enforce locking.
[/quote]
I am not really sure what you mean with high level architectural choices and therefore I don't understand what difference between those and low level locks.


As for the size of the vector: I cannot make predictions about this.
The vector has pointers to objects used in the Physics Engine I'm making. And as the world can contain any number of objects from 0 to the maximum range of an unsigned integer.

As for other functions accessing the data.
There is are two functions that can modify the data:
- A master function (There will only one running at once), this will wait for all child threads to finish.
- A child function (There will be mutiple of these running at once), this is the function my question is about and will not modify any data inside the vector or modify the vector itself.

If there is anything more you'd like to know, please ask.

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