Sharing an Enumerator Across Threads

Shared enum between multiple threads

In this scenario Interlocked wouldn't be useful. Your series of if/then checks depend on the value of _action remaining unchanged as they all execute. Otherwise _action==Action.Read could be false, but before the next statement executes _action is set to Action.Read and all of the other conditions are false.

You'd want to use lock to ensure that nothing modifies _action while those statements are executing.

So you might have an object for your lock:

private readonly _lockObject = new object();

And then when _action is getting set:

lock(_lockObject)
{
_action = newValue;
}

And when executing your conditions you could just read the value of _action within the lock and then release it. That way the lock is held for the shortest time possible. If _action gets modified while you're executing your conditions you won't be affected because you've created a separate value and you're no longer depending on the value of _action.

Action action;
lock(_lockObject)
{
action = _action
}
if (action == Action.Read)
{
}
else if (action == Action.Write)
{
}
else if (action == Action.None)
{
}
else
{
}

Multi-threading in Enumerator

Yes, some locking required. you can achieve it using lock or using a concurrent collection type.

using lock:

ProcessItem(Item item)
{
if(item.prop == "somevalue")
{
lock(_list)
{
_list.Add(item);
}
}
}

Edit: based on detail you provided, you can wrap the enumerator from external lib using your own enumerator like below so you can use Parallel.ForEach on it:

We assume the enumerator you got is something like MockEnumerator, we wrap it in a normal IEnumerator, and IEnumerable so we are able to use Parallel.ForEach to read in parallel.

class Program
{
class Item
{
public int SomeProperty { get; }

public Item(int prop)
{
SomeProperty = prop;
}
}

class MockEnumerator
{
private Item[] _items = new Item[] { new Item(1), new Item(2) };
private int _position = 0;

public bool Next()
{
return _position++ < _items.Length;
}

public Item Read()
{
return _items[_position];
}
}

class EnumeratorWrapper : IEnumerator<Item>, IEnumerable<Item>
{
private readonly MockEnumerator _enumerator;

public EnumeratorWrapper(MockEnumerator enumerator)
{
this._enumerator = enumerator;
}

public Item Current => _enumerator.Read();

object IEnumerator.Current => Current;

public void Dispose()
{
}

public IEnumerator<Item> GetEnumerator()
{
throw new NotImplementedException();
}

public bool MoveNext()
{
return _enumerator.Next();
}

public void Reset()
{
}

IEnumerator IEnumerable.GetEnumerator()
{
return this;
}
}

private static List<Item> _list = new List<Item>();

static void Main(string[] args)
{
var enumerator = new EnumeratorWrapper(new MockEnumerator());
Parallel.ForEach(enumerator, item =>
{
if (item.SomeProperty == 1)//someval
{
lock (_list)
{
_list.Add(item);
}
}
});
}
}

Thread-safe enumeration of shared memory that can be updated or deleted

I'll treat your questions as a request for feedback which helps you learn. Let me address the three solutions you have already identified:

  1. Yes, this is why such a design should never be exposed as an API to a 3rd-party (or even other developers). It is tricky to use correctly. This codeproject article has some nasty advice.
  2. Much better because this model would be explicit about locking, not implicit. However this violates separation of concerns in my opinion.
  3. Not sure what you mean here. You could have a Snapshot() method on your dictionary which does a read-only copy which can be safely passed around and read. This is a different trade-off than solution 1.

There is a different solution entirely: Use an immutable dictionary. Such a dictionary could be passed around, read and enumerated safely even under concurrent write access. Such dictionaries/maps are commonly implemented using trees.

I'll elaborate more on a key point: You need to think about the concurrent system as a whole. You cannot make you app correct by making all components thread-safe (in your case a dictionary). You need to define, what you are using the dictionary for.

You say:

The reason why I want to iterate over the collection at runtime is
that I want to find the values, that matches some criteria.

You you have concurrent writes happening to the data and want to get a consistent snapshot atomically from the dictionary (maybe to shot some progress report in the UI?). Now that we know this goal, we can devise a solution:

You could add a Clone method to your dictionary which clones all data while taking the read-lock. This will give the caller a fresh object which it can then enumerate over independently. This would be a clean and safely exposable API.

Share a List T between multiple threads

You should definitely lock when iterating over it too - if the list is changed while you're iterating over it, an exception will be thrown.

From the docs for List<T>.GetEnumerator:

The enumerator does not have exclusive access to the collection; therefore, enumerating through a collection is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you can 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.

Additionally, even a single read from a List<T> isn't thread-safe if you could be writing to it as well - even if it doesn't fail, there's no guarantee that you'll get the most recent value.

Basically, List<T> is only safe for multiple threads if it's not written to after the last point at which its state becomes visible to all threads.

If you want a thread-safe collection, and if you're using .NET 4 or higher, take a look at the System.Collections.Concurrent namespace.

C# Queue and Enumerator with 2 threads

Yes, you will have problems with 2 threads and simple Queue. Advice - use ConcurrentQueue. It's thread safe.

Thread A:

toDoReuqests.Enqueue("test");

Thread B:

string retValue;

while(!toDoReuqests.TryDequeue(out retValue))
{
Console.WriteLine("Tolto dalla coda B");
Console.WriteLine(retValue);
}

For removing items from queue use methods Dequeue or TryDequeue.

is enumerator thread safe after getting with lock?

No, not at all. This lock synchronizes only access to _list.GetEnumerator method; where as enumerating a list is lot more than that. It includes reading the IEnumerator.Current property, calling IEnumerator.MoveNext etc.

You either need a lock over the foreach(I assume you enumerate via foreach), or you need to make a copy of list.

Better option is to take a look at Threadsafe collections provided out of the box.

In c# , how to iterate IEnumerable in multithreading environment

According to the docs you should be able to use the GetEnumerator() method of ConcurrentDictionary to get a thread-safe iterator.

The enumerator returned from the dictionary is safe to use concurrently with reads and writes to the dictionary, however it does not represent a moment-in-time snapshot of the dictionary. The contents exposed through the enumerator may contain modifications made to the dictionary after GetEnumerator was called.

Since you're dealing with concurrent threads, it's not surprising to have some tradeoffs with consistency, but I would expect this approach to block less than the brute force approach given in other answers. This wouldn't have worked if you tried:

var items = concurrentDict.Items.ToList();

but it's supposed to work for

var items = concurrentDict.GetEnumerator();

or you could simply reference the iterator directly:

foreach(var item in concurrentDict)
{
valueList.Add(item.Value);
}

IEnumerable T thread safety?

IEnumerable<T> can't be modified. So what can be non thread safe with it? (If you don't modify the actual List<T>).

For non thread safety you need writing and reading operations.

"Iterator in itself" is instantiated for each foreach.

Edit: I simplified my answer a bit, but @Eric Lippert added valuable comment. IEnumerable<T> doesn't define modifying methods, but it doesn't mean that access operators are thread safe (GetEnumerator, MoveNext and etc.) Simplest example: GetEnumerator implemented as this:

  • Every time returns same instance of IEnumerator
  • Resets it's position

More sophisticated example is caching.

This is interesting point, but fortunately I don't know any standard class that has not thread-safe implementation of IEnumerable.



Related Topics



Leave a reply



Submit