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:
- 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.
- Much better because this model would be explicit about locking, not implicit. However this violates separation of concerns in my opinion.
- 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
Cast Between String and Classname
Ruby on Rails: How to Add a CSS File with Rails Project
React Error (Only a Reactowner Can Have Refs.)
Gem Install Cannot Find a Header File
Redirect_Uri_Mismatch. Login with Google Using Ruby on Rails
"Encoding::Invalidbytesequenceerror" Error Occurs in Rails 3.1.0
Handling Iframe with Capybara Ruby
Ruby: Ssl_Connect Syscall Returned=5 Errno=0 State=Unknown State (Openssl::Ssl::Sslerror)
Rails 3. Simple_Format Do Not Wrap Result in Paragraph Tags
"No Routes Matches" When Using Current_Page in Rails 3
Override "Show" Resource Route in Rails
In Ruby, Why Is a Method Invocation Not Able to Be Treated as a Unit When "Do" and "End" Is Used
Rvm and Osx Lion - Rvm 'Forgets' Gemsets on System Restart
Error Loading Media: File Could Not Be Played Error in Jw_Player Rails
How to Make Like Clause Case-Insensitive
How to Write an Rspec Test for a Ruby Method That Contains "Gets.Chomp"