12

If I initialize a HashSet<> inside a Lazy initializer and then never change the contents, is that HashSet<> inherently threadsafe? Are there read actions that require locking?

Similar Java question here for collections generally, which essentially says yes, but with some caveats (that don't apply in this situation).

Community
  • 1
  • 1
Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • 1
    Yes again, if it truly doesn't change and while being constructed isn't accessed, then yes you will be fine. That said, there is nothing stopping someone from changing it, so its "readonly" behaviour is unenforced. Try the immutable collections. – Adam Houldsworth Nov 25 '14 at 15:58
  • @AdamHouldsworth it's unfortunate that the immutable collections have horrendous performance http://ayende.com/blog/164739/immutable-collections-performance – Chris Marisic Feb 26 '15 at 15:25
  • @ChrisMarisic The poor performance is quite well known. That said for lots of scenarios the performance issues would not surface. You could always implement your own by wrapping the non-immutable collections internally, which have had many optimisations applied to them over the years. – Adam Houldsworth Feb 27 '15 at 08:08
  • @AdamHouldsworth how would wrapping something slow ever make it faster? Not to mention hold on to immutability? It's absurdly hard to be immutable in c#. However the code they talk about in Ayende's comments provides interesting constructs to roll your own. – Chris Marisic Feb 27 '15 at 15:24
  • @ChrisMarisic I meant wrap something like an array or a dictionary in order to roll your own, featuring an API that doesn't allow for change once constructed. I wasn't referring to wrapping the existing immutable types. – Adam Houldsworth Mar 01 '15 at 09:00

1 Answers1

13

Yes, it is. As long as the construction of the HashSet object is thread safe, accessing it will always be thread safe as long as the contents doesn't change.

If you initialize the Lazy using LazyThreadSafetyMode.PublicationOnly you can be sure the initialization of the Lazy is thread safe.

When multiple threads try to initialize a Lazy<T> instance simultaneously, all threads are allowed to run the initialization method (or the default constructor, if there is no initialization method). The first thread to complete initialization sets the value of the Lazy<T> instance. That value is returned to any other threads that were simultaneously running the initialization method, unless the initialization method throws exceptions on those threads.

A little code sample:

var l = new Lazy<HashSet<string>>( () => new HashSet<string>() { "a" }
                                 , LazyThreadSafetyMode.PublicationOnly
                                 );
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • 2
    While I believe that is, in practice, true, I cannot find a guarantee that non-modifying HashSet operations are threadsafe. In fact the HashSet documentation states otherwise: https://msdn.microsoft.com/en-us/library/bb359438(v=vs.110).aspx "Any instance members are not guaranteed to be thread safe." Do you have evidence that threadsafety is guaranteed in this case? – Pagefault Apr 14 '16 at 23:56