Currently there is only TryAdd but I have a use case where there may be an item with the same key and I want to replace the value with my new value.
Possible options would be:
- Array access like you can use on Dictionary: “container[ key ] = value;”
- A dedicated function: “container.Replace( key, value );”
- A concurrent remove: “container.Remove( key );”
Option 3 is less desirable, but in my use case I know that the other jobs in the parallel for will not be writing to the same key so it would be safe for me to first remove then TryAdd.
1 Like
Might be worth checking if TryAdd actually follows the idiomatic approach here of returning false if there is an existing value. I would assume it does, but given the lack of a TryRemove it does make one wonder.
Edit: It does return false on existing entry
Both replace and remove are trickier to get right in a completely thread safe way. In order to be safe we need to make the entire operation visible with a single atomic write.
Remove would have to find the previous node in the list and update its next pointer to next-next, but there are some corner-cases when two jobs are removing two consecutive nodes in the list at the same time which will be really tricky to get right. Think the chain A → B → C → D, we are removing B while some other jobs is removing C. We would have to read the next pointer from B and write it as the next pointer in A. But the other job might write the next pointer in B after we read it which would give us a broken list.
Replace can be slightly easier to implement. In order for replace to work we would have to either remove the old value and add a new - which runs into the remove case - or we would have to write the updated data in the existing node. If we write the data to an existing node we need to write it as one atomic operation since two jobs might be calling update at the same time. We do not have atomic operations for anything larger than pointer size so the cases where it could be used is very limited. Another option could be to take some kind of light weight per key lock when updating the value, which is hard to get right without affecting performance and/or memory.
So to sum up. It seems possible to implement a Replace method, but it is not on our short term priority list right now so if you need it now I’m afraid your best bet would be to implement it yourself - which is possible since it is all written in c#.
1 Like
Thanks for the replies. I ended up writing to a different container the operations that I want to do, then a job that is scheduled after the ParallelFor deals with that which is fine as it’s not doing parallel operations on the map.
It would be impossible to guarantee determinism with threads racing to remove values in general.