Thread Safety of C# List<T> for readers
Asked Answered
B

3

13

I am planning to create the list once in a static constructor and then have multiple instances of that class read it (and enumerate through it) concurrently without doing any locking.

In this article http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx MS describes the issue of thread safety as follows:

Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

A List can support multiple readers concurrently, as long as the collection is not modified. Enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with one or more write accesses, the only way to ensure thread safety is to lock the collection during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.

The "Enumerating through a collection is intrinsically not a thread-safe procedure." Statement is what worries me.

Does this mean that it is thread safe for readers only scenario, but as long as you do not use enumeration?

Or is it safe for my scenario?


Thanks for the answers. Why do I need to use AsReadOnly at all if it will work with or without it?

Balduin answered 12/5, 2010 at 21:46 Comment(0)
S
9

They mean that if you enumerate a collection while a different thread (or your own thread) changes it, you'll have problems.

As long as you don't change the collection at all, and as long as you don't share IEnumerators across threads, you shouldn't have any problems.

Snakebird answered 12/5, 2010 at 21:50 Comment(2)
By don't share IEnumerators across threads do you mean don't access **the same enumerator instance** from multiple threads?Piles
@EugeneBeresovksy: Exactly.Snakebird
S
3

Yes, the list is safe for a readers only scenario, if the list will never be modified then it will be ok.

If it is in fact the case that after construction the list will not be modified, then you should use a more appropriate interface like ReadOnlyCollection. if it is stored in a public static variable as you say you should use that interface.

private static List<T> shared_list;

private static ReadOnlyCollection<T> _data;
public static IEnumerable<T> Data
{
    get
    {
        return _data ?? (_data = shared_list.AsReadOnly());
    }
}

====EDIT====

This version caches the ReadOnlyCollection reference for faster future lookup times.

Note, there is a possibility for a data race to occur on the _data variable if two threads try to grab a reference concurrently when it is null, but because everything is read only this doesn't really matter, we will just create an extra ReadOnlyCollection object, which is cheap compared to synchronizing.

Stephainestephan answered 12/5, 2010 at 21:53 Comment(3)
Note that you still must not change the original list. Also, don't call AsReadOnly every time you get the property.Snakebird
AsReadOnly is an O(1) operation according to the MSDN documentation. I believe it simply wraps with a ReadOnlyCollection which simply delegates to the underlying collection. So AsReadOnly should be pretty quick, but it does cause an allocation so we could potentially share that memory. ill edit to reflect.Stephainestephan
Under .NET 4 you could use the generic Lazy class for initialization. See msdn.microsoft.com/en-us/library/dd642329.aspxSubdiaconate
C
1

Does this mean that it is thread safe for readers only scenario, but as long as you do not use enumeration? Or is it safe for my scenario?

Depends entirely on when you write to the collection. That cannot coincide with reading (enumerating) without some kind of locking scheme.

So if you fill it once and then only iterate over it, you're safe. But when one thread changes the list (add or remove items) you will need for example a ReaderWriterLockSlim.

When you change the state of a stored item, the thread-safety is with that item (not the list).

Cenis answered 12/5, 2010 at 21:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.