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?