Multiple locking task (threading) - c#

I need to implement the class that should perform locking mechanism in our framework.
We have several threads and they are numbered 0,1,2,3.... We have a static class called ResourceHandler, that should lock these threads on given objects. The requirement is that n Lock() invokes should be realeased by m Release() invokes, where n = [0..] and m = [0..]. So no matter how many locks was performed on single object, only one Release() call is enough to unlock all. Even further if o object is not locked, Release() call should perform nothing. Also we need to know what objects are locked on what threads.
I have this implementation:
public class ResourceHandler
{
private readonly Dictionary<int, List<object>> _locks = new Dictionary<int, List<object>>();
public static ResourceHandler Instance {/* Singleton */}
public virtual void Lock(int threadNumber, object obj)
{
Monitor.Enter(obj);
if (!_locks.ContainsKey(threadNumber)) {_locks.Add(new List<object>());}
_locks[threadNumber].Add(obj);
}
public virtual void Release(int threadNumber, object obj)
{
// Check whether we have threadN in _lock and skip if not
var count = _locks[threadNumber].Count(x => x == obj);
_locks[threadNumber].RemoveAll(x => x == obj);
for (int i=0; i<count; i++)
{
Monitor.Exit(obj);
}
}
// .....
}
Actually what I am worried here about is thread-safety. I'm actually not sure, is it thread-safe or not, and it's a real pain to fix that. Am I doing the task correctly and how can I ensure that this is thread-safe?

Your Lock method locks on the target objects but the _locks dictionary can be accessed by any thread at any time. You may want to add a private lock object for accessing the dictionary (in both the Lock and Release methods).
Also keep in mind that by using such a ResourceHandler it is the responsibility of the rest of the code (the consuming threads) to release all used objects (a regular lock () block for instance covers that problem since whenever you leave the lock's scope, the object is released).
You also may want to use ReferenceEquals when counting the number of times an object is locked instead of ==.

You can ensure this class is thread safe by using a ConcurrentDictionary but, it won't help you with all the problems you will get from trying to develop your own locking mechanism.
There are a number locking mechansims that are already part of the .Net Framework, you should use those.
It sounds like you are going to need to use a combination of these, including Wait Handles to achieve what you want.
EDIT
After reading more carefully, I think you might need an EventWaitHandle

What you have got conceptually looks dangerous; this is bacause calls to Monitor.Enter and Monitor.Exit for them to work as a Lock statement, are reccomended to be encapsulated in a try/finally block, that is to ensure they are executed sequetally. Calling Monitor.Exit before Monitor.Enter will throw an exception.
To avoid these problems (if an exception is thrown, the lock for a given thread may-or-may-not be taken, and if a lock is taken it will not be released, resulting in a leaked lock. I would recomend using one of the options provided in the other answers above. However, if you do want to progress with this mechanism, CLR 4.0 added the following overload to the Monitor.Enter method
public static void Enter (object, ref bool lockTaken);
lockTaken is false if and only if the Enter method throws an exception and the lock was not taken. So, using your two methods using a global bool lockTaken you can create something like (here the example is for a single locker - you will need a Dictionary of List<bool> corresponding to your threads - or event better a Tuple). So in your method Lock you would have something like
bool lockTaken = false;
Monitor.Enter(locker, ref lockTaken);
in the other method Release
if (lockTaken)
Monitor.Exit(locker);
I hope this helps.
Edit: I don't think I fully appreciate your problem, but from what I can gather I would be using a Concurrent Collection. These are fully thead safe. Check out IProducerConsumerCollection<T> and ConcurrentBag<T>. These should facilitate what you want with all thread safter taken care of by the framework (note. a thread safe collection doesn't mean the code it executes is thread safe!). However, using a collection like this, is likely to be far slower than using locks.

IMO you need to use atomic set of functions to make it safe.
http://msdn.microsoft.com/en-us/library/system.threading.mutex.aspx
Mutexes I guess will help u.

Related

Conditional thread lock in c#

Is it possible to have a conditional thread lock when the underlying condition is not constant?
I have two functions A and B, and a condition to decide which function to execute.
A is thread safe by itself, multiple calls to A can execute simultaneously, B is not, and is Synchronized. But during execution of B the condition can change (from false to true) and therefore all threads executing A at that time will throw errors.
if (condition)
{
A();
}
else
{
B();
}
A - thread safe
B - Synchronized using [MethodImpl(MethodImplOptions.Synchronized)]
Therefore, I am looking for a way to lock A but only when B is running.
Please suggest a way to achieve this.
Some elaborations:
I am creating a cache, and performance is very crucial, thus a blanket lock is not feasible.
Condition is whether or not the requested data is present in the cache.
A() = AddToUpdates() - Executed on a cache hit, just adds to the number of updates for a particular cache key, using a concurrent dictionary.
B() = ProccessUpdates() and EvictLeastPriorityEntry() - Executed on a cache miss, all previous updates will be processed and the underlying data structure storing the ordering of cache entries will be re-arranged.
And then the entry with least priority will be removed.
As mentioned in the accepted answer ReaderWriterLock seems to be the way to go.
Just one problem though,
Let's say, thread1 starts execution and a cache hit occurs, (on the entry with the least priority) meaning the if condition is true and enters the if block. But before calling A(), control is switched to thread2.
thread2 - cache miss occurs, reordering and eviction (Entry which A() from thread1 needed access to) is performed.
Now when controlled is returned to thread1, error will occur.
This is the solution I feel should work:
_lock.EnterReadLock();
if (condition)
{
A();
}
_lock.ExitReadLock();
if (!condition)
{
B();
}
void A()
{
// ....
}
void B()
{
_lock.EnterWriteLock();
// ...
_lock.ExitWriteLock();
}
Will this work?
Thank you.
I possible solution to your problem might be the ReaderWriterLockSlim class. This is a synchronization primitive that allows multiple concurrent readers, or one exclusive writer, but not both of those at the same time.
Use ReaderWriterLockSlim to protect a resource that is read by multiple threads and written to by one thread at a time. ReaderWriterLockSlim allows multiple threads to be in read mode, allows one thread to be in write mode with exclusive ownership of the lock, and allows one thread that has read access to be in upgradeable read mode, from which the thread can upgrade to write mode without having to relinquish its read access to the resource.
Example:
private readonly ReaderWriterLockSlim _lock = new();
void A()
{
_lock.EnterReadLock();
try
{
//...
}
finally { _lock.ExitReadLock(); }
}
void B()
{
_lock.EnterWriteLock();
try
{
//...
}
finally { _lock.ExitWriteLock(); }
}
Your question looks a lot like this:
A() is some read only method, so thread safe. Different execution of A in parallel is OK.
B() is like writing/mutating things that A method uses. So A() becomes not thread safe if executed at same time.
For example B() could write in a List and A() executions read on this list. And you would get exception "InvalidOperationException: Collection Was Modified" thrown from A() .
I advise you to look for "producer/consumer problem" in google and look for the tons of example there are.
But in case you absolutely want to begins B execution while A execution(s) has/have not terminated, you can add checkpoint in A() using Monitor class, it is used to lock a resource and synchronize with other threads. It is more complex though and i would go first for producer/consumer pattern to see if it fill the needs
Some more things:
I would check is the use of BlockingCollection<T> class that may fit your exact need too (and is easy to use)
The use of MethodImplOptions.Synchronized is not recommended because it use public lock. We use usually use private lock (object readonly _lock = new object();) so no one except the maintainer of this object can lock on it, thus preventing dead lock (and preventing other people accusing your code of a bug because other people locked your instance of class without knowing you do the same internally)

Locking on the object that is being synchronized or using a dedicated lock object? [duplicate]

This question already has answers here:
C# lock statement, what object to lock on?
(4 answers)
Closed 6 years ago.
As far as I've understood from colleagues and the web, it is bad practice to lock on the object that is being synchronized, but what I dont understand is why?
The following class is supposed to load settings to a dictionary, and it has a method to retrieve settings as well.
public class TingbogSettingService : ITingbogSettingService
{
private readonly ISettingRepository _settingRepository;
private readonly ICentralLog _centralLog;
private Dictionary<string, ISetting> _settingDictionary = new Dictionary<string, ISetting>();
public TingbogSettingService(ISettingRepository settingRepository, ICentralLog centralLog)
{
_settingRepository = settingRepository;
_centralLog = centralLog;
}
public ISetting GetSetting(string settingName)
{
ISetting setting;
if (!_settingDictionary.TryGetValue(settingName, out setting))
{
return null;
}
return setting;
}
public void LoadSettings()
{
var settings = _settingRepository.LoadSettings();
try
{
lock (_settingDictionary)
{
_settingDictionary = settings.ToDictionary(x => x.SettingName);
}
}
catch (Exception ex)
{
_centralLog.Log(Targets.Database, LogType.Error, $"LoadSettings error: Could not load the settings", new List<Exception>() { ex });
}
}
}
During the LoadSettings function I want to lock the _settingDictionary, so that GetSetting will be blocked, until the new settings are loaded.
Should I use a dedicated lock object instead?
For instance:
private static readonly object m_Lock = new object();
…
lock (m_Lock)
EDIT
I thought that lock(_settingDictionary) would lock the _settingDictionary itself, however I now realize that his is not the case. What I wanted was to prevent other threads from accessing _settingDictionary until the new settings were loaded (LoadSettings method completed). As only 1 thread is updating the _settingDictionary, I guess I dont need a lock there at all.
As for closing the question - something similar has been asked before, yes, but the scenario is not the same. I learned from your answers and it is going to be hard to pick a winner amongst y'all.
This is quite a broad subject, but let me focus on one major problem in your code: _settingDictionary changes.
You don't lock on the field, you lock on the instance. This means that when you lock on _settingDictionary, and then you change _settingDictionary, you're not preventing any concurrent access - anyone can lock on the new _settingDictionary.
lock doesn't prevent access to the object you're locking either. If you need synchronization, you must synchronize all access to the object, including your _settingDictionary.TryGetValue. Dictionary isn't thread-safe.
The main guide-lines to what you should lock on are something like this:
The lock object is private to the locker - if it's not private, some other class might be holding a lock on your object, which may lead to deadlocks.
The field should be readonly - this is not a strict requirement, but it makes things easier. The main point is that you must not lock on an object that might change while the lock is being held; others trying to take the lock concurrently will succeed.
The lock object is a reference type - this kind of goes without saying, but you cannot lock on e.g. an int field, since it is boxed when you try to lock it - in effect, this is the same as the previous point - everyone locks on their own instance of the object, eliminating all synchronization.
Obligatory disclaimer: Multi-threading is hard. Seriously hard. Make sure you understand what's happening and what can possibly happen. Any multi-threaded code you write must be written in a way that's correct, first and foremost. http://www.albahari.com/threading/ is a great starter on all things multi-threaded in C#/.NET.
There is no "right" or "wrong" answer to this but there are some guidelines and some things to be aware of.
First, there's many that feel that Microsoft should never have allowed to lock on arbitrary objects. Instead they should've encapsulated the locking functionality into a specific class and avoided potential overhead in every other object out there.
The biggest problem with allowing locking on arbitrary objects is that if you lock on an object you make publicly available to 3rd party code, you have no control over who else might be locking on the same object. You could write your code to the letter, dotting every I and it would still end up deadlocking because some other, 3rd party, code is locking on the same object out of your control.
So that point alone is guideline enough to say "don't ever lock on objects you make publicly available".
But what if the object you want to synchronize access to is private? Well, then it becomes more fuzzy. Presumably you have full control over the code you write yourself and thus if you then lock on the dictionary, as an example, then it will work just fine.
Still, my advice would be to always set up a separate object to lock on, get into this habit, and then you won't so easily make mistakes if you later decides to expose a previously private object into the public and forgetting to separate the locking semantics from it.
The simplest locking object is just that, an object:
private readonly object _SomethingSomethingLock = new object();
Also know, though I think you already do, that locking on an object does not "lock the object". Any other piece of code that doesn't bother with locks can still access the object just fine.
Here is also something I just noticed about your code.
When you do this:
lock (x)
You don't lock on x, you lock on the object that x refers to at the time of the lock.
This is important when looking at this code:
lock (_settingDictionary)
{
_settingDictionary = settings.ToDictionary(x => x.SettingName);
}
Here you have two objects in play:
The dictionary that settingDictionary refers to at the time of lock (_settingDictionary)
The new dictionary that .ToDictionary(...) returns
You have a lock on the first object, but not on the second. This is another scenario where having a dedicated locking object would not only make sense, but also be correct, as the above code is buggy in my opinion.
The problem you are talking about happens when you lock on an object to which external users of your class have access - most commonly, the object itself, i.e. lock (this).
If your code were locking on this instead of _settingDictionary, someone else could deadlock your code as follows:
TingbogSettingService svc = ...
lock (svc) {
Task.Run(() => {
svc.LoadSettings();
});
}
When you lock on a private object, such as _settingDictionary in your case, there harmful effect described above is avoided, because nobody outside your code can lock on the same object.
Note: Using the lock in your code does not make it thread-safe, because GetSetting method does not lock on _settingDictionary when reading from it. Moreover, the fact that you re-assing _settingDictionary inside the lock makes locking irrelevant, because after the reassignment another thread can enter protected section in the lock.
There are different thing you could lock:
a dedicated non static object: private readonly object m_Lock = new object();
a dedicated static object (your example): private static readonly object m_Lock = new object();
the object itself: lock (_settingDictionary)
this, typeof(MyClass)...
The first two are OK but actually different. Locking on a static object means the lock is shared between all instances of your classes. Locking on a non-static object means the lock is different for each instance of your class.
The third option is OK, it's the same as the first one. The only difference is that the object is not read-only (using a read-only field is slightly better as you ensure it won't ever change).
The last option is a bad option for various reasons, see Why is lock(this) {...} bad?
So be careful about what you lock, your example uses a static object while your initial code uses a non-static object. Those are really different use cases.
It is better to use a dedicated object that is not modified by the block of code or used for other purposes in some other methods. That way the object has a single responsibility so that you don't mix the usage of it as a synchronization object, with it being maybe set to null at some point or reinitialized by another method.
lock (_settingDictionary) doesn't lock the dictionary specified between (), it locks the next block of code by using _settingDictionary as a synchronization object (To know if the block has been entered of left by another thread by setting some flags on that object).

Lock and refresh implementation

I have a key to task mapping and I need to run the task only if the task for the given is not already running. Pseudo code follows. I believe there is lot of scope for improvement. I'm locking on the map and hence almost serializing access to CacheFreshener. Is there a better way of doing this? We know that when I'm trying to lock a key k1, there is no point in cache freshener call for key k2 waiting for lock.
class CacheFreshener
{
private ConcurrentDictionary<string,bool> lockMap;
public RefreshData(string key, Func<string, bool> cacheMissAction)
{
lock(lockMap)
{
if (lockMap.ContainsKey(key))
{
// no-op
return;
}
else
{
lockMap.Add(key, true);
}
}
// if you are here means task is not already present
cacheMissAction(key);
lock(lockMap) // Do we need to lock here??
{
lockMap.Remove(key);
}
}
}
As requested, here is an elaborated explanation of what I was getting at relative to my comments…
The basic issue here seems to be the question of concurrency, i.e. two or more threads accessing the same object at a time. This is the scenario ConcurrentDictionary is designed for. If you use the IDictionary methods of ContainsKey() and Add() separately, then you would need explicit synchronization (but only for that operation…in this particular scenario it wouldn't strictly be needed when calling Remove()) to ensure these are performed as a single atomic operation. But the ConcurrentDictionary class anticipates this need, and includes the TryAdd() method to accomplish the same, without the explicit synchronization.
<aside>
It is not entirely clear to me the intent behind the code example as given. The code appears to be meant to only store an object in the "cache" for the duration of the invocation of the cacheMissAction delegate. The key is removed immediately after. So it does seem like it's not really caching anything per se. It just prevents more than one thread from being in the process of invoking cacheMissAction at a time (subsequent threads will fail to invoke it, but also cannot count on it having completed by the time their call to the RefreshData() method has completed).
</aside>
But taking the code example as given, it's clear that no explicit locking is actually required. The ConcurrentDictionary class already provides thread-safe access (i.e. non-corruption of the data structure when used concurrently from multiple threads), and it provides the TryAdd() method as a mechanism for adding a key (and its value, though here that's just always a bool literal of true) to the dictionary that will ensure that only one thread ever has a key in the dictionary at a time.
So we can rewrite the code to look like this instead and accomplish the same goal:
private ConcurrentDictionary<string,bool> lockMap;
public RefreshData(string key, Func<string, bool> cacheMissAction)
{
if (!lockMap.TryAdd(key, true))
{
return;
}
// if you are here means task was not already present
cacheMissAction(key);
lockMap.Remove(key);
}
No lock statement is needed for either the add or remove, as the TryAdd() handles the entire "check for key and add if not present" operation atomically.
I will note that using a dictionary to do the job of a set could be considered inefficient. If the collection is likely not to be large, it's no big deal, but I do find it odd that Microsoft chose to make the same mistake they made originally when in the pre-generics days you had to use the non-generic dictionary object Hashtable to store a set, before HashSet<T> came along. Now we have all these easy-to-use classes in System.Collections.Concurrent, but no thread-safe implementation of ISet<T> in there. Sigh…
That said, if you do prefer a somewhat more efficient approach in terms of storage (this is not necessarily a faster implementation, depending on the concurrent access patterns of the object), something like this would work as an alternative:
private HashSet<string> lockSet;
private readonly object _lock = new object();
public RefreshData(string key, Func<string, bool> cacheMissAction)
{
lock (_lock)
{
if (!lockSet.Add(key))
{
return;
}
}
// if you are here means task was not already present
cacheMissAction(key);
lock (_lock)
{
lockSet.Remove(key);
}
}
In this case, you do need the lock statement, because the HashSet<T> class is not inherently thread-safe. This is of course very similar to your original implementation, just using the more set-like semantics of HashSet<T> instead.

C# Is this method thread safe?

Consider the following code:
Dictionary<string, string> list = new Dictionary<string, string>();
object lockObj = new object();
public void MyMethod(string a) {
if (list.Contains(a))
return;
lock (lockObj) {
list.Add(a,"someothervalue");
}
}
Assuming I'm calling MyMethod("mystring") from different threads concurrently.
Would it be possible for more than one thread (we'll just take it as two) enter the if (!list.Contains(a)) statement in the same time (with a few CPU cycles differences), both threads get evaluated as false and one thread enters the critical region while another gets locked outside, so the second thread enters and add "mystring" to the list again after the first thread exits, resulting in the dictionary trying to add a duplicate key?
No, it's not thread-safe. You need the lock around the list.Contains too as it is possible for a thread to be switched out and back in again between the the if test and adding the data. Another thread may have added data in the meantime.
You need to lock the entire operation (check and add) or multiple threads may attempt to add the same value.
I would recommend using the ConcurrentDictionary(TKey, TValue) since it is designed to be thread safe.
private readonly ConcurrentDictionary<string, string> _items
= new ConcurrentDictionary<string, string>();
public void MyMethod(string item, string value)
{
_items.AddOrUpdate(item, value, (i, v) => value);
}
You need to lock around the whole statement. It's possible for you to run into issues on the .Contains portion (the way your code is now)
You should check the list after locking. e.g.
if (list.Contains(a))
return;
lock (lockObj) {
if (list.Contains(a))
return;
list.Add(a);
}
}
private Dictionary<string, string> list = new Dictionary<string, string>();
public void MyMethod(string a) {
lock (list) {
if (list.Contains(a))
return;
list.Add(a,"someothervalue");
}
}
Check out this guide to locking, it's good
A few guidelines to bear in mind
Generally lock around a private static object when locking on multiple writeable values
Do not lock on things with scope outside the class or local method such as lock(this), which could lead to deadlocks!
You may lock on the object being changed if it is the only concurrently accessed object
Ensure the object you lock is not null!
You can only lock on reference types
I am going to assume that you meant write ContainsKey instead of Contains. Contains on a Dictionary is explicitly implemented so it is not accessible via the type you declared.1
Your code is not safe. The reason is because there is nothing preventing ContainsKey and Add from executing at the same time. There are actually some quite remarkable failure scenarios that this would introduce. Because I looked at how the Dictionary is implemented I can see that your code could cause a situation where data structure contains duplicates. And I mean it literally contains duplicates. The exception will not necessarily be thrown. The other failure scenarios just keep getting stranger and stranger, but I will not go into those here.
One trivial modification to your code might involve a variation of the double-checked locking pattern.
public void MyMethod(string a)
{
if (!dictionary.ContainsKey(a))
{
lock (dictionary)
{
if (!dictionary.ContainsKey(a))
{
dictionary.Add(a, "someothervalue");
}
}
}
}
This, of course, is not any safer for the reason I already stated. Actually, the double-checked locking pattern is notoriously difficult to get right in all but the simplest cases (like the canonical implementation of a singleton). There are many variations on this theme. You can try it with TryGetValue or the default indexer, but ultimately all of these variations are just dead wrong.
So how could this be done correctly without taking a lock? You could try ConcurrentDictionary. It has the method GetOrAdd which is really useful in these scenarios. Your code would look like this.
public void MyMethod(string a)
{
// The variable 'dictionary' is a ConcurrentDictionary.
dictionary.GetOrAdd(a, "someothervalue");
}
That is all there is to it. The GetOrAdd function will check to see if the item exists. If it does not then it will be added. Otherwise, it will leave the data structure alone. This is all done in a thread-safe manner. In most cases the ConcurrentDictionary does this without waiting on a lock.2
1By the way, your variable name is obnoxious too. If it were not for Servy's comment I may have missed the fact that we were talking about a Dictionary as opposed to a List. In fact, based on the Contains call I first thought we were talking about a List.
2On the ConcurrentDictionary readers are completely lock free. However, writers always take a lock (adds and updates that is; the remove operation is still lock free). This includes the GetOrAdd function. The difference is that the data structure maintains several possible lock options so in most cases there is little or no lock contention. That is why this data structure is said to be "low lock" or "concurrent" as opposed to "lock free".
You can first do a non-locking check, but if you want to be thread-safe you need to repeat the check again within the lock. This way you don't lock unless you have to and ensure thread safety.
Dictionary<string, string> list = new Dictionary<string, string>();
object lockObj = new object();
public void MyMethod(string a) {
if (list.Contains(a))
return;
lock (lockObj) {
if (!list.Contains(a)){
list.Add(a,"someothervalue");
}
}
}

Under what conditions can a thread enter a lock (Monitor) region more than once concurrently?

(question revised): So far, the answers all include a single thread re-entering the lock region linearly, through things like recursion, where you can trace the steps of a single thread entering the lock twice. But is it possible somehow, for a single thread (perhaps from the ThreadPool, perhaps as a result of timer events or async events or a thread going to sleep and being awaken/reused in some other chunk of code separately) to somehow be spawned in two different places independently of each other, and hence, run into the lock re-entrance problem when the developer didn't expect it by simply reading their own code?
In the ThreadPool Class Remarks (click here) the Remarks seem to suggest that sleeping threads should be reused when they're not in use, or otherwise wasted by sleeping.
But on the Monitor.Enter reference page (click here) they say "It is legal for the same thread to invoke Enter more than once without it blocking." So I figure there must be something I'm supposed to be careful to avoid. What is it? How is it even possible for a single thread to enter the same lock region twice?
Suppose you have some lock region that takes an unfortunately long time. This might be realistic, for example, if you access some memory that has been paged out (or whatever.) The thread in the locked region might go to sleep or something. Does the same thread become eligible to run more code, which might accidentally step into the same lock region? The following does NOT, in my testing, get multiple instances of the same thread to run into the same lock region.
So how does one produce the problem? What exactly do you need to be careful to avoid?
class myClass
{
private object myLockObject;
public myClass()
{
this.myLockObject = new object();
int[] myIntArray = new int[100]; // Just create a bunch of things so I may easily launch a bunch of Parallel things
Array.Clear(myIntArray, 0, myIntArray.Length); // Just create a bunch of things so I may easily launch a bunch of Parallel things
Parallel.ForEach<int>(myIntArray, i => MyParallelMethod());
}
private void MyParallelMethod()
{
lock (this.myLockObject)
{
Console.Error.WriteLine("ThreadId " + Thread.CurrentThread.ManagedThreadId.ToString() + " starting...");
Thread.Sleep(100);
Console.Error.WriteLine("ThreadId " + Thread.CurrentThread.ManagedThreadId.ToString() + " finished.");
}
}
}
Suppose you have a queue that contains actions:
public static Queue<Action> q = whatever;
Suppose Queue<T> has a method Dequeue that returns a bool indicating whether the queue could be successfully dequeued.
And suppose you have a loop:
static void Main()
{
q.Add(M);
q.Add(M);
Action action;
while(q.Dequeue(out action))
action();
}
static object lockObject = new object();
static void M()
{
Action action;
lock(lockObject)
{
if (q.Dequeue(out action))
action();
}
}
Clearly the main thread enters the lock in M twice; this code is re-entrant. That is, it enters itself, through an indirect recursion.
Does this code look implausible to you? It should not. This is how Windows works. Every window has a message queue, and when a message queue is "pumped", methods are called corresponding to those messages. When you click a button, a message goes in the message queue; when the queue is pumped, the click handler corresponding to that message gets invoked.
It is therefore extremely common, and extremely dangerous, to write Windows programs where a lock contains a call to a method which pumps a message loop. If you got into that lock as a result of handling a message in the first place, and if the message is in the queue twice, then the code will enter itself indirectly, and that can cause all manner of craziness.
The way to eliminate this is (1) never do anything even slightly complicated inside a lock, and (2) when you are handling a message, disable the handler until the message is handled.
Re-Entrance is possible if you have a structure like so:
Object lockObject = new Object();
void Foo(bool recurse)
{
lock(lockObject)
{
Console.WriteLine("In Lock");
if (recurse) { foo(false); }
}
}
While this is a pretty simplistic example, it's possible in many scenarios where you have interdependent or recursive behaviour.
For example:
ComponentA.Add(): locks a common 'ComponentA' object, adds new item to ComponentB.
ComponentB.OnNewItem(): new item triggers data-validation on each item in list.
ComponentA.ValidateItem(): locks a common 'ComponentA' object to validate the item.
Same-thread re-entry on the same lock is needed to ensure you don't get deadlocks occurring with your own code.
One of the more subtle ways you can recurse into a lock block is in GUI frameworks. For example, you can asynchronously invoke code on a single UI thread (a Form class)
private object locker = new Object();
public void Method(int a)
{
lock (locker)
{
this.BeginInvoke((MethodInvoker) (() => Method(a)));
}
}
Of course, this also puts in an infinite loop; you'd likely have a condition by which you'd want to recurse at which point you wouldn't have an infinite loop.
Using lock is not a good way to sleep/awaken threads. I would simply use existing frameworks like Task Parallel Library (TPL) to simply create abstract tasks (see Task) to creates and the underlying framework handles creating new threads and sleeping them when needed.
IMHO, Re-entering a lock is not something you need to take care to avoid (given many people's mental model of locking this is, at best, dangerous, see Edit below). The point of the documentation is to explain that a thread cannot block itself using Monitor.Enter. This is not always the case with all synchronization mechanisms, frameworks, and languages. Some have non-reentrant synchronization in which case you have to be careful that a thread doesn't block itself. What you do need to be careful about is always calling Monitor.Exit for every Monitor.Enter call. The lock keyword does this for you automatically.
A trivial example with re-entrance:
private object locker = new object();
public void Method()
{
lock(locker)
{
lock(locker) { Console.WriteLine("Re-entered the lock."); }
}
}
The thread has entered the lock on the same object twice so it must be released twice. Usually it is not so obvious and there are various methods calling each other that synchronize on the same object. The point is that you don't have to worry about a thread blocking itself.
That said you should generally try to minimize the amount the time you need to hold a lock. Acquiring a lock is not computationally expensive, contrary to what you may hear (it is on the order of a few nanoseconds). Lock contention is what is expensive.
Edit
Please read Eric's comments below for additional details, but the summary is that when you see a lock your interpretation of it should be that "all activations of this code block are associated with a single thread", and not, as it is commonly interpreted, "all activations of this code block execute as a single atomic unit".
For example:
public static void Main()
{
Method();
}
private static int i = 0;
private static object locker = new object();
public static void Method()
{
lock(locker)
{
int j = ++i;
if (i < 2)
{
Method();
}
if (i != j)
{
throw new Exception("Boom!");
}
}
}
Obviously, this program blows up. Without the lock, it is the same result. The danger is that the lock leads you into a false sense of security that nothing could modify state on you between initializing j and evaluating the if. The problem is that you (perhaps unintentionally) have Method recursing into itself and the lock won't stop that. As Eric points out in his answer, you might not realize the problem until one day someone queues up too many actions simultaneously.
ThreadPool threads cannot be reused elsewhere just because they went to sleep; they need to finish before they're reused. A thread that is taking a long time in a lock region does not become eligible to run more code at some other independent point of control. The only way to experience lock re-entry is by recursion or executing methods or delegates inside a lock that re-enter the lock.
Let's think about something other than recursion.
In some of business logics, they would like to control the behaviors of synchronization.
One of these patterns, they invoke Monitor.Enter somewhere and would like to invoke Monitor.Exit elsewhere later. Here is the code to get the idea about that:
public partial class Infinity: IEnumerable<int> {
IEnumerator IEnumerable.GetEnumerator() {
return this.GetEnumerator();
}
public IEnumerator<int> GetEnumerator() {
for(; ; )
yield return ~0;
}
public static readonly Infinity Enumerable=new Infinity();
}
public partial class YourClass {
void ReleaseLock() {
for(; lockCount-->0; Monitor.Exit(yourLockObject))
;
}
void GetLocked() {
Monitor.Enter(yourLockObject);
++lockCount;
}
void YourParallelMethod(int x) {
GetLocked();
Debug.Print("lockCount={0}", lockCount);
}
public static void PeformTest() {
new Thread(
() => {
var threadCurrent=Thread.CurrentThread;
Debug.Print("ThreadId {0} starting...", threadCurrent.ManagedThreadId);
var intanceOfYourClass=new YourClass();
// Parallel.ForEach(Infinity.Enumerable, intanceOfYourClass.YourParallelMethod);
foreach(var i in Enumerable.Range(0, 123))
intanceOfYourClass.YourParallelMethod(i);
intanceOfYourClass.ReleaseLock();
Monitor.Exit(intanceOfYourClass.yourLockObject); // here SynchronizationLockException thrown
Debug.Print("ThreadId {0} finished. ", threadCurrent.ManagedThreadId);
}
).Start();
}
object yourLockObject=new object();
int lockCount;
}
If you invoke YourClass.PeformTest(), and get a lockCount greater than 1, you've reentered; not necessarily be concurrent.
If it was not safe for reentrancy, you will get stuck in the foreach loop.
In the code block where Monitor.Exit(intanceOfYourClass.yourLockObject) will throw you a SynchronizationLockException, it is because we are trying to invoke Exit more than the times it have entered. If you are about to use the lock keyword, you possibly would not encounter this situation except directly or indirectly of recursive calls. I guess that's why the lock keyword was provided: it prevents the Monitor.Exit to be omitted in a careless manner.
I remarked the calling of Parallel.ForEach, if you are interested then you can test it for fun.
To test the code, .Net Framework 4.0 is the least requirement, and following additional name spaces are required, too:
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
using System.Collections;
Have fun.

Categories

Resources