Click here to Skip to main content
15,887,676 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi, I need to write thread safe class
whereby making a copy of my class object and using it in other
thread must be safe, however if one wants to access the same class object
from different threads, it is not required to be safe.
I can't relate these two issues, why/and how are they different?
What do I have to consider to achieve what I wrote above?
Thank you.
Posted
Comments
Ian A Davidson 27-Mar-13 14:14pm    
You make it sound like there are two irreconcilable statements here, but it is only because your assertion is incorrect. If "one wants to access the same object from different threads" then the object MUST be thread safe.

It sounds like you need to lock the contents of your class for writing while it's being copied.
To do this you'll need to go through a series of steps.

First check that your class has no public data members, public functions are fine but if you have public data members you basically can't secure your class for multi threading. Move any public data to protected, add any necessary Get and Set functions and patch up any calling code to use them til everything is back in order.

Add a locking primitive to the protected data of your class. You haven't sadi what OS or framework if any you're using so I can't know exactly what you should use but assuming you're on Windows and just using the Win32 API lookup CRITICAL_SECTION and add one of those to your class, along with the necessary code to initialise it in all the constructors and close it in the destructor. If you don't have a custom default constructor and destructor you'll need to add them.

Now add protected Lock and Unlock functions to your class which lock and unlock, acquire and release, enter and leave the locking primitive you've chosen to use.

Finally you'll need a custom copy constructor which locks the source instance by calling it's lock function before making the copy, and unlocks it afterwards.

If you want the standard copy constructor signature that takes a const reference you'll need to make your CRITICAL_SECTION or equivalent locking primitive member mutable and your Lock and Unlock functions const so that you can call then on a const reference, or do it the quick and dirty way and cast away the constness by you didn't hear that from me. :-)

Now you have an object that can be duplicated but you still need to go through all the functions that modify data, all the Setters, but not the other constructors and Lock your primitive at the beginning of each one, unlocking it again at the end. Only then will your class be really safe for multi threading.

The hard part about multi threading is that it isn't right at all until it's perfect but on the upside it's the hardest thing you'll ever do in programming so if you can do it you can do anything.
 
Share this answer
 
Comments
nv3 27-Mar-13 17:30pm    
That is a nice and detailed description of how to proceed. Really well done.
I'd like to clarify one of your points: The locking must be done in both, reading and writing member functions. If you only aqcuire a lock in the writing code, you might be catching a reading thread right in the middle of things. The rule to follow is: One critical section for object that must be protected. And everyone has to aqcuire the lock before even looking at the object.

And I'd like to add that there a couple nice functions in Win32 like InterlockedIncrement that perform the increment in an atomic operation and you can use them without acquiring a lock first. Very helpful for little getter/setter functions.

Pardon my comments. You did a good job laying out the route to go and I am sure the OP will be happy with it.
Matthew Faithfull 27-Mar-13 19:03pm    
Thanks and you're right of course if you want any level of data integrity at all in a free threaded scenario you must lock for read as well as write. In the OPs case it looks like the only parallel reads would be those needed to do the copy construction. As long as no writes occur while that's happenning you can snapshot a valid state of the object to duplicate it for use by a second thread. I think was the situation of the OP.
Dan page 30-Mar-13 10:28am    
hi Matthew thank you for an elaborate answer. My class has some get and also set methods, so anyone can read at any point in time. I could set locks (I use mutex btw) at the start of each Set function and unlock it at the end as you suggest (maybe do similar for Get-ers?). Just my confusion was, there are two situations it appears: (1) one to make some class thread safe in case many threads try to access the SAME object, and (2) make a copy of the instance of that class and usage of that copy in other thread should be safe. The (2)-nd issue is what I want to achieve and my problem was that I can not differentiate between the two, what do I need to do to achieve (2)? And why is it different from (1)??
Matthew Faithfull 30-Mar-13 10:42am    
Assuming we've covered case 1 then the answer depends on what exactly you mean in case 2 by 'make a copy of the instance'. If you mean make a copy of an existing instance e.g.
Object* pnewObj = new Object( Object& ReferenceToExistingInstanceToBeCopied );
The case 2 is the same as case 1, you still have to fully protect everything because of the access to the source instance ReferenceToExistingInstanceToBeCopied, in the copy constructor.
If you mean 'make a new instance' e.g.
Object* pNewInstance = new Object();
Then you don't need to protect anything except any static members that are shared by instances of Object. You don't need to protect anything because nothing can know about the new object except the thread constructing it until after it's constructed. As long as only that thread uses is and never exposes the address to any data accessible from another thread then the object is 'local' and doesn't need to be protected as only the 'owning' thread will ever access it. Objects on the stack follow the same rules as every thread has it's own local stack.
I hope this clears it up.
Dan page 30-Mar-13 11:33am    
Dear Matthew sorry, maybe I didn't highlight well enough: We don't want to achieve (1) actually - we may discard it, we just want to achieve (2).
The whole idea "it is not required to be safe" is wrong in case of multithreading.

When someone says "it is not required to be safe" it simply means "this call will never be done from more than one thread" or even "it cannot be called by more than one thread, so, if someone needs thread safety, the call should be wrapped in appropriate lock or mutex statement".

So, now you know how issues are related. What should you do in the multithreading situation? In most cases, you need to guard the access to some object using mutual exclusion. On Windows, if you do it in the same process, it can be lightweight version of it called CriticalSection. If you need to access to the same object from different processes, this is more complex: you will need IPC, and, for mutual exclusion, you will need a named mutex. Please see:
http://en.wikipedia.org/wiki/Mutual_exclusion[^],
http://en.wikipedia.org/wiki/Inter-process_communication[^].

I only mentioned basic approaches. At least this is how you can guarantee thread safety. Don't even play with the idea of having threads and not thread-safety. If cannot work. Or, much worse, it may seemingly work, but for a while, which is a great danger. Don't even think about it.

On top of thread safety, you can consider more delicate design issue related to performance. As people say, best synchronization is no synchronization. That said, you need to provide 100% of thread safety but make it as fine-grain as possible. Also, it depends very much on how much you read the state of the object and how much you modify it. More complex thread synchronization objects can greatly improve performance in the situation where threads often read the state of the object but its modification is seldom.

It is important not to over-synchronize things. In this forum, I faced some "solution" where people synchronized the thread so much that they actually could only execute one at a time. Again, make synchronization as fine-grain as possible, yet 100% guaranteed. Some education and experience required.

—SA
 
Share this answer
 
v2
Comments
nv3 27-Mar-13 17:31pm    
My 5 for the good advice of the experienced hand.
Sergey Alexandrovich Kryukov 27-Mar-13 17:57pm    
Thank you very much,
—SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900