Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is returning an IEnumerable<> thread-safe?

I have a Visual Studio 2008 C# .NET 3.5 project where I want to have a thread-safe pool of Foo objects.

public class FooPool
{
    private object pool_lock_ = new object();
    private Dictionary<int, Foo> foo_pool_ = new Dictionary<int, Foo>();

    // ...

    public void Add(Foo f)
    {
        lock (pool_lock_)
        {
            foo_pool_.Add(SomeFooDescriminator, f);
        }
    }

    public Foo this[string key]
    {
        get { return foo_pool_[key]; }
        set { lock (pool_lock_) { foo_pool_[key] = value; } }
    }

    public IEnumerable<Foo> Foos
    {
        get
        {
            lock (pool_lock_)
            {
                // is this thread-safe?
                return foo_pool_.Select(x => x.Value);
            }
        }
    }
}

Is the public IEnumerable<Foo> Foos { get; } function thread-safe? Or, do I need to clone the result and return a new list?

like image 886
PaulH Avatar asked Apr 19 '12 16:04

PaulH


2 Answers

No, it isn't.

If another thread adds to the dictionary while your caller enumerates that, you'll get an error.

Instead, you can do:

lock (pool_lock_) {
    return foo_pool.Values.ToList();
}
like image 139
SLaks Avatar answered Nov 15 '22 07:11

SLaks


Is the IEnumerable<Foo> Foos { get; } function thread-safe?

No.

Or, do I need to clone the result and return a new list?

No, because that's not right either. A threadsafe method that gives the wrong answer is not very useful.

If you lock and make a copy then the thing you are returning is a snapshot of the past. The collection could be changed to be completely different the moment the lock is released. If you make this threadsafe by making a copy then you are now handing a bag full of lies to your caller.

When you are dealing with single-threaded code, a reasonable model is that everything is staying the same unless you take specific measures to change a thing. That is not a reasonable model in multi-threaded code. In multi-threaded code, you should assume the opposite: everything is constantly changing unless you take specific measures (such as a lock) to ensure that things are not changing. What is the good of handing out a sequence of Foos that describe the state of the world in the distant past, hundreds of nanoseconds ago? The entire world could be different in that amount of time.

like image 20
Eric Lippert Avatar answered Nov 15 '22 07:11

Eric Lippert