Do locks ensure the thread safety of list elements? - c#

To my understanding, they do not. Please confirm.
Suppose I have a Processor object that contains a List<T> where T is a reference type.
The UI thread instantiates the Processor object, and periodically calls into it to get the List<T>.
The processor object also starts a task that has a reference to the List<T> when it is constructed.
A lock is used to reserve access to the List<T> in the getter and in the task to ensure they have exclusive access to the List<T>.
public class Processor
{
private List<T> _list = new List<T>();
private Object _listLock = new Object();
public Processor()
{
// populate _list
Task t = new Task(Process);
t.Start();
}
public List<T> GetList()
{
lock(_listLock)
{
return _list;
}
}
private void Process()
{
while(!doneProcessing)
{
lock(_listLock)
{
// access and modify _list items
}
Thread.Sleep(...);
}
}
}
But even if List<T> is locked in the getter, and it returned the list reference without issue, the task started by the Processor is still modifying the reference type list elements when it seizes the lock.
The elements of the list are still subject to change from the Processor’s task, and accessing them in the UI thread would not be thread safe.
If I am correct, an obvious solution is to have the getter return a new list populated with deep copies of the list elements.
public List<T> GetList()
{
lock(_listLock)
{
return _list.Select(t => t.Clone()).ToList();
}
}
What else can you do?

Locks, used the way you have, will not ensure thread safety.
If you're trying for thread safety, consider using one of the thread safe collections available in the .NET CLR.
You'll notice there is no thread-safe IList. Here's why. The very notion of a thread safe list doesn't make a lot of sense. But with some minor design changes you could easily use something like a ConcurrentDictionary or ConcurrentBag.

Your GetList() doesn't do what you think it does:
public List<T> GetList()
{
lock(_listLock)
{
return _list;
}
}
Since GetList() just returns a reference to _list, the lock() does nothing except prevent 2 threads from getting a reference to _list at the same time, which is not an issue anyway.
The issue is that you are passing a reference to a list object back to the UI thread, and the elements in the list pointed to by the reference could change at any time, event while the UI thread is iterating over the elements, which is not good.
You could expose the lock object to the UI thread, but this means your UI will need to block while the list is being updated, which is usually undesirable (blocking UI thread degrades user experience).
Depending on what your UI does with the list, it might be better to take an immutable snapshot of your list and return that to the UI.
If you are wanting to keep the UI in step with changes to the underlying list data, then the approach you take really depends on the UI technology.

Related

How lock work for static collection

The lock keyword is used where you want to achieve that the area should be executed by at most on thread, in a multithreading environment, rest of the thread will wait for the area.
I have a collection IList<Student> student=new List<Student>() that is being used in multiple classes.
In some places objects are getting added to the list, and in some places objects are getting deleted. This causes some inconsistent behavior.
Is it true that when I lock the collection in class x in a multithreading environment, the collection will be locked for all classes and all threads in different classes will wait for the lock?
Class StaticClass
{
Public static IList<Student> student=new List<Student>();
}
Class ClassA
{
//add an item in the collection
}
Class ClassB
{
//delete an item in the collection
}
Class ClassC
{
//lock the collection here
lock (StaticClass.student)
{
foreach (ConnectionManager con in ConnectionManager.GetAllStudents())
{
con.Send(offlinePresence);
}
}
}
When I have locked the collection in ClassC, will other threads for classA and ClassB wait? Until the for loop execute nobody is allowed to add or delete items in the collection, because the collection has been locked?
Take a look at System.Collections.Generic.SynchronizedCollection<T> from the System.ServiceModel.dll assembly. All of the locking stuff is built in.
As Fabio said, ConcurrentBag also works, but a separate list context is created for each thread accessing it. When you try and remove items, it works like a queue. Once you've run out of items in your own thread's list, it will then "steal" items from other threads' lists (in a thread-safe way).
For your task, I'm guessing the SynchronizedCollection would be better.
When your code acquires a lock on an object, it does not block access to that object from other threads. The only thing that happens to this object is that other threads cannot acquire a lock to the same object as long as the lock is not released.
It is common practice to reserve a separate plain object as the target of a lock.
In your example other threads can still access the List<Student> object as long as code does not explicitly lock it first.
Locking can cause serious performance issues (threads waiting for eachother) and in many cases does not need to be implemented explicitly. Just take a look at the Concurrent collection classes of the .NET Framework.

locking by target object self

I'm curious about the difference below two example.
case 1) locking by readonly object
private readonly object key = new object();
private List<int> list = new List<int>;
private void foo()
{
lock(key){
list.add(1);
}
}
case2) locking by target object itself
private List<int> list = new List<int>;
private void foo()
{
lock(list){
list.add(1);
}
}
are both cases thread-safe enough? i'm wondering if garbage collector changes the address of list variable(like 0x382743 => 0x576382) at sometime so that it could fail thread-safe.
So long as List<T> does not have within its code any lock(this) statements the two functions will behave the same.
However, because you don't always know if a object locks on itself or not without looking through it's source code it is safer to just lock on a separate object.
One thing of note, classes that inherit from ICollection have a SyncRoot property which is explicitly the object you are supposed to lock on if you want to put a lock on the collection without using a seperate object.
private List<int> list = new List<int>;
private void foo()
{
lock(((ICollection)list).SyncRoot){
list.add(1);
}
}
This internally is just doing the same thing as you did and created a separate new Object() to lock on.
In both cases, foo() is thread-safe. But locking on separate readonly object (case 1) is preferred, because
you don't have to bother about whether list is not null (lock would then fail)
you don't have to bother about whether list is re-assigned (assigning new instance of List<T> to list could cause some troubles, e.g. loss of atomicity of locked block of code)
you don't have to bother about whether you pass list to third-party code (e.g. as result of some function), as it is good practice to lock only on objects you have under your exclusive control (it could lead to deadlock if another piece of could would lock on this object too).
when lock protects more objects at once (e.g. list1, list2, etc.), locking just on one of them would still work (if you would lock consistently on the same object everywhere), but would lead to less readable and harder to understand code.
Lock is guaranteed to work properly even if garbage collector moves object you are locking on.

Make a linked list thread safe

I know this has been asked before (and I will keep researching), but I need to know how to make a particular linked list function in a thread safe manner. My current issue is that I have one thread that loops through all elements in a linked list, and another may add more elements to the end of this list. Sometimes it happens that the one thread tries to add another element to the list while the first is busy iterating through it (which causes an exception).
I was thinking of just adding a variable (boolean flag) to say that the list is currently busy being iterated through, but then how do I check it and wait with the second thread (it is ok if it waits, as the first thread runs pretty quickly). The only way I can think of doing this is through the use of a while loop constantly checking this busy flag. I realized this was a very dumb idea as it would cause the CPU to work hard while doing nothing useful. And now I am here to ask for a better insight. I have read about locks and so on, but it does not seem to be relevant in my case, but perhaps I am wrong?
In the meanwhile I'll keep searching the internet and post back if I find a solution.
EDIT:
Let me know if I should post some code to clear things up, but I'll try and explain it more clearly.
So I have a class with a linked list in it that contains elements that require processing. I have one thread that iterates through this list through a function call (let's call it "processElements"). I have a second thread that adds elements to process in a non-deterministic manner. However, sometimes it happens that it tries to call this addElement function while the processElements is running. This means that the an element is being added to the linked list while it is being iterated through by the first thread. This is not possible and causes an exception. Hope this clears it up.
I need the thread that adds new elements to yield until the processElements method is done executing.
To anyone stumbling on this problem. The accepted answer will give you a quick, an easy solution, but check out Brian Gideon's answer below for a more comprehensive answer, which will definitely give you more insight!
The exception is likely the result of having the collection changed in the middle of an iteration via IEnumerator. There are few techniques you can use to maintain thread-safety. I will present them in order of difficultly.
Lock Everything
This is by far the easiest and most trivial method for getting access to the data structure thread-safe. This pattern works well when the number of read and write operations are equally matched.
LinkedList<object> collection = new LinkedList<object>();
void Write()
{
lock (collection)
{
collection.AddLast(GetSomeObject());
}
}
void Read()
{
lock (collection)
{
foreach (object item in collection)
{
DoSomething(item);
}
}
}
Copy-Read Pattern
This is a slightly more complex pattern. You will notice that a copy of the data structure is made prior to reading it. This pattern works well when the number of read operations are few compared to the number of writes and the penalty of the copy is relatively small.
LinkedList<object> collection = new LinkedList<object>();
void Write()
{
lock (collection)
{
collection.AddLast(GetSomeObject());
}
}
void Read()
{
LinkedList<object> copy;
lock (collection)
{
copy = new LinkedList<object>(collection);
}
foreach (object item in copy)
{
DoSomething(item);
}
}
Copy-Modify-Swap Pattern
And finally we have the most complex and error prone pattern. I actually do not recommend using this pattern unless you really know what you are doing. Any deviation from what I have below could lead to problems. It is easy to mess this one up. In fact, I have inadvertently screwed this one up as well in the past. You will notice that a copy of the data structure is made prior to all modifications. The copy is then modified and finally the original reference is swapped out with the new instance. Basically we are always treating collection as if it were immutable. This pattern works well when the number of write operations are few compared to the number of reads and the penalty of the copy is relatively small.
object lockobj = new object();
volatile LinkedList<object> collection = new LinkedList<object>();
void Write()
{
lock (lockobj)
{
var copy = new LinkedList<object>(collection);
copy.AddLast(GetSomeObject());
collection = copy;
}
}
void Read()
{
LinkedList<object> local = collection;
foreach (object item in local)
{
DoSomething(item);
}
}
Update:
So I posed two questions in the comment section:
Why lock(lockobj) instead of lock(collection) on the write side?
Why local = collection on the read side?
Concerning the first question consider how the C# compiler will expand the lock.
void Write()
{
bool acquired = false;
object temp = lockobj;
try
{
Monitor.Enter(temp, ref acquired);
var copy = new LinkedList<object>(collection);
copy.AddLast(GetSomeObject());
collection = copy;
}
finally
{
if (acquired) Monitor.Exit(temp);
}
}
Now hopefully it is easier to see what can go wrong if we used collection as the lock expression.
Thread A executes object temp = collection.
Thread B executes collection = copy.
Thread C executes object temp = collection.
Thread A acquires the lock with the original reference.
Thread C acquires the lock with the new reference.
Clearly this would be disasterous! Writes would get lost since the critical section is entered more than once.
Now the second question was a little tricky. You do not necessarily have to do this with the code I posted above. But, that is because I used the collection only once. Now consider the following code.
void Read()
{
object x = collection.Last;
// The collection may get swapped out right here.
object y = collection.Last;
if (x != y)
{
Console.WriteLine("It could happen!");
}
}
The problem here is that collection could get swapped out at anytime. This would be an incredibly difficult bug to find. This is why I always extract a local reference on the read side when doing this pattern. That ensure we are using the same collection on each read operation.
Again, because problems like these are so subtle I do not recommend using this pattern unless you really need to.
Here’s a quick example of how to use locks to synchronize your access to the list:
private readonly IList<string> elements = new List<string>();
public void ProcessElements()
{
lock (this.elements)
{
foreach (string element in this.elements)
ProcessElement(element);
}
}
public void AddElement(string newElement)
{
lock (this.elements)
{
this.elements.Add(element);
}
}
A lock(o) statement means that the executing thread should acquire a mutual-exclusion lock on the object o, execute the statement block, and finally release the lock on o. If another thread attempts to acquire a lock on o concurrently (either for the same code block or for any other), then it will block (wait) until the lock is released.
Thus, the crucial point is that you use the same object for all the lock statements that you want to synchronize. The actual object you use may be arbitrary, as long as it is consistent. In the example above, we’re declared our collection to be readonly, so we can safely use it as our lock. However, if this were not the case, you should lock on another object:
private IList<string> elements = new List<string>();
private readonly object syncLock = new object();
public void ProcessElements()
{
lock (this.syncLock)
{
foreach (string element in this.elements)
ProcessElement(element);
}
}
public void AddElement(string newElement)
{
lock (this.syncLock)
{
this.elements.Add(element);
}
}

List<T> multiple writer thread safety

If I have multiple threads calling the Add method of a List object, and no readers, do I only need to lock on the List object before calling Add to be thread safe?
Usually it's best to lock on a separate (immutable) object... locking on the same object you're modifying is bad practice should be done with caution.
private readonly object sync = new object();
private List<object> list = new List<object>();
void MultiThreadedMethod(object val)
{
lock(sync)
{
list.Add(val);
}
}
In a basic case like this you will not have a problem, but if there is a possibility that your list can be changed (not the contents of the list, but the list itself), then you might have a situation where you lock on two objects when you only intend to lock on one.
Yes. But you might also consider subclassing the List and "new" over the Add method. That will allow you to encapsulate the lock. It will work great as long as nothing accesses the base List. This technique is used for simple tree structures in XNA video games.
Yes, you need to lock. Instance methods are not guaranteed to be thread safe on List<T>.
I think these have been linked before on here, but I found them to be very useful and interesting:
Thread safe collections are hard
Thread safe collection

How make custom Thread Safe Generic List return the whole list in C#?

I am a threading noob and I am trying to write a custom thread safe generic list class in C# (.NET 3.5 SP1). I've read Why are thread safe collections so hard?. After reviewing the requirements of the class I think I only need to safely add to the list and return the list. The example shows pretty much all I want except it lacks the return list method therefore I've written my own public method like below:
Update: based on suggestions given I've reviewed my requirements and therefore simplified the class to as below:
public sealed class ThreadSafeList<T>
{
private readonly IList<T> list = new List<T>();
private readonly object lockable = new object();
public void Add(T t)
{
lock (lockable)
{
list.Add(t);
}
}
public IList<T> GetSnapshot()
{
IList<T> result;
lock (lockable)
{
result = new List<T>(list);
}
return result;
}
}
Agree with #jrista. There's a semantics issue you need to resolve, and why is it called Translate()? What is the intent?
A - current code - return a read-only wrapper of the internal list
return new ReadOnlyCollection<T>(list);
You still have threading issues if the original list is changed if another thread is iterating over the list. As long as you're aware of this, it isn't a huge issue.
B - a read-only copy.
return new List<T>(list).AsReadOnly();
This list has no threading issues because nothing modifies the new list. The only reference held is by the ReadOnlyCollection<T> wrapper.
C - a normal (writable) copy
return new List<T>(list);
Returns a new list, and the caller can do what they wish to their list without affecting the original list, and changes to the original list do not affect this list.
Does it matter if another consumer grabs a copy of the list and then modifies their copy? Do consumers need to see changes to the list? Do you just need a thread-safe enumerator?
public IEnumerator<T> ThreadSafeEnumerator()
{
List<T> copy;
lock(lockable)
copy = new List<T>(list);
foreach (var value in copy)
yield return value;
}
In my experience you have to use your brain when it comes to thread-safety and not rely on solutions such as these.
in short, it depends on what the receiver of the list is going to be doing with it.
The Translate() method looks correct. Using the lock you are preventing others from adding or otherwise modifying your list while you are in Translate/AddRange.
I think there might be a problem with your IsReadyOnly property though. You use a lock when reading/writing the property internally. But there also is a public getter which is not locked. It might happen that thread 1 calls MarkAsReadOnly while a second thread might still get false when looking at IsReadOnly. I'd use a normal property instead and either lock in the getter or use a volatile bool field.
You can use SynchronizedCollection.
http://msdn.microsoft.com/en-us/library/ms668265.aspx

Categories