C # locks and multithreading

Some newbies are asking questions about multithreading in .NET that I think will help reinforce some of the concepts I'm trying to absorb - I read a few multithreaded materials (including the Albahari e-book), but I feel like I just need some confirmation of some questions, to help manage these concepts at home

  • The lock area protects the general area of ​​the code — suppose there is a thread executing a method that increments a simple integer variable x in the loop — however, it will not protect the code elsewhere, which can also change the variable x, for example, in another method to another thread. .

    Since these are two different areas of the code that potentially affect the same variable, do we solve this by blocking both areas of the code using the same blocking variable for both binding areas around the variable x? If you blocked both regions of the code with different locking variables, would this not protect the variable correctly?

  • To continue this example, using the same blocking variable, what happens if for some reason the code in one method goes into some infinite loop and never abandons the lock variable - how could the second area of ​​code in another method detect this?

  • How does choosing a lock variable affect lock behavior? I have already read numerous posts on this subject, but I can never find a final answer - in some cases people explicitly use an object variable specifically for this purpose, sometimes people use a lock (this), and finally there were times when I saw how people use an object of type.

    How do different variants of lock variables affect the behavior / scope of the lock, and which scenarios make sense to use one above the other?

  • Suppose you have a hashtable wrapped in a class that displays add, remove, get and some Calculate method (say, each object represents a quantity, and this method sums each value), and all these methods are locked - however, as soon as the reference object in this collection becomes accessible to other code and is transferred around the application, this object (and not the hash table) will now be outside the lock area surrounding the methods of this class. How could you then protect access / updates to those actual objects taken from a hash table that might interfere with the Calculate method?

Appreciate any heuristics that will help me reinforce these concepts - thanks!

+7
source share
2 answers

1) You are right. You must use the same lock object to protect two different areas of the code, which, for example, increment the x variable.

2) This is known as a dead end and is one of the difficulties with multi-threaded programming. There are algorithms that can be used to prevent deadlocks, such as the bankers algorithm.

3) Some languages ​​simplify locking, for example, in .Net, you can simply create an object and use it as a general lock. This is good for code synchronization as part of this process. Lock (this) simply applies the lock to the object in question. However, try to avoid this, instead create a private object and use it. Locking (this) can lead to a deadlock situation. The lock object below it is probably just a wrapper around the critical section. If you want to protect the resource in different processes, you will need a much heavier Mutex, which requires locking the kernel object and is expensive, so do not use it if you should not.

4) You need to make sure that blocking is also applied there. But, of course, when people call methods this reference, they call methods that use synchronization.

+1
source

1) Yes

2) This is a dead end

3) The parts of your code that you want to block are implementation details of your class. Detecting a lock object using lock(this) or lock(this.GetType()) causes problems, because now external code can lock the same object and lock your code inadvertently or maliciously. The lock object must be closed.

4) It’s not entirely clear what you mean, of course, you don’t want to expose the Hashtable directly. Just save it as a private field of the class, encapsulating it.

However, the likelihood that you can safely expose your class to client code using threads decreases very quickly with the number of public methods and properties that you expose. You will quickly get to the point where only client code can correctly lock the lock. The fine-grained locking creates many opportunities for the streaming race when client code holds onto property values. Say the return value of the Count property. By the time the value is used, as in the for loop, the Count property could have been changed. Only the most thorough design can escape these traps, a serious headache.

In addition, fine-grained locking is very inefficient, as it inevitably occurs in most of the internal parts of your code. Locks are not so expensive, about 100 cpu cycles, but they add up quickly. Especially wasted if the class object is not actually used in multiple threads.

You then have no option, but declare your class stream unsafe, and client code should use it in a thread-safe manner. Also, the main reason many .NET classes are not thread safe. This is the biggest reason that threads are so hard to get right, the programmer, the least likely to do it right, is responsible for doing the most difficult thing.

+2
source

All Articles