I have a program with a number (~10) of threads. Every time a variable can be accessed by multiple threads ("shared variables") I protect their read and write with tRtlCriticalSection and it works fine. Still it raised me some questions. The first two are clear yes/no, the third is more opinion/taste/implementation based, but I am also interested in that one.
Question 1 - Do I need to protect every read?
E.g. I have two threads, A and B and they both access a variable V (let it be a large record for the example). A only reads it Va := V; while B can read and write it as well Vb := V; and V := Vb;. Currently I protect all three operations, but I am wondering if Vb := V; needs to be protected at all.
It is clear that V := Vb; needs to be protected, otherwise Va := V; might read a half updated, inconsistent record. It only makes sense if Va := V; is protected with the same CriticalSection.
However, do I need to protect Vb := V;? Thread A never updates it, so if both A and B read V at the same time, it is not supposed to be a problem. B cannot read and write V at the same time, so there is no conflict there either. So why would I protect B reading V (other then being future proof, should I change my mind and A would start to write V as well, etc.)?
Question 2 - Do I need to protect every V?
In Q1 V was a large record. but, what if SizeOf(V) is less than or equal to the processor bit length, e.g. a boolean or a natural integer type? I would assume that V := Vb; is such a simple processor operation that it cannot happen (event theoretically, not because it is "unlikely") that A starts reading while V is half written. E.g. in a 64 bit operating system V being Int64, can it happen that the first two bytes are already updated when B reads the whole variable. This I cannot test, because even if theoretically it is possible, it is so unlikely...
What if on a 64 bit system, V is a 32 bit integer? What is with larger things, e.g. a record or an AnsiString?
Question 3 - How many Critical Sections to declare?
If I have V and W like above. Does it make sense to use CriticalSectionV and CriticalSectionW to protect the two separately? Obviously if they are updated together and their consistency is important (e.g. X and Y coordinates of a point) then they have to be updated, read and hence protected in one critical section.
But if they are totally independent then I can use two separate CSs and so reading V does not block writing W. What are the things to consider to choose the best option? I would assume something like, that if the operations are fast (just V := Va;) then it is better to use one CS even for unrelated variables, not to make the whole program too complex and not to load the OS with many CSs to monitor. However, if the operations are large and long (e.g. reading a whole file from disk) then it is better to use separate CSs, e.g. not to block reading of a variable while writing a file.
The same can be extended to arrays. E.g. I have an array of records. If the length of the array changes during the operations, then I am sure we need to have one CS for the whole array (so when B changing the length of the array makes the system to move it in memory, then A does not try to read it), but what if the array length is fixed? Would it make sense to include a tRtlCriticalSection field in the record declaration and hence protect every record independently? Is it technically doable, or there can be cases when such an approach fails? If technically possible, how "expensive" is it to have hundreds of CSs? Does it worth it?