Can we add new elements to a list using Parallel.ForEach()?

泄露秘密 提交于 2020-01-02 05:32:15

问题


My code does very simple stuff

list already has elements. I have approximately 25000 elements (and I'm expecting to have more) in the list and each element is small (DateTime).

List<DateTime> newList = new List<DateTime>();
Parallel.ForEach(list, l => newlist.Add(new DateTime(l.Ticks + 5000)));

i.e, based on each element, I'm creating new elements and adding them to a different list. But, this doesn't seem to be a good programming approach. I hit this exceptions some times, but not everytime.

IndexOutOfRangeException : {"Index was outside the bounds of the array."}

Can we add elements to a list using Parallel.ForEach()? If yes, why do I hit the error? If no, why?


回答1:


What you would really want in this situation is more like this:

newlist = list.AsParallel().Select(l => new DateTime(l.Ticks + 5000)).ToList();

Although you should measure the performance to see if this situation even benefits from parallelization.




回答2:


Try a thread local variable with a final result that adds all thread local variables to the newList as such...

Parallel.ForEach(list, () => DateTime.MinValue, (l, state, date) =>
{
    date = new DateTime(l.Ticks+5000);
    return date;
},
finalresult =>
{
   lock (newList)
   {
       newList.Add(finalresult);
   }
});

The first param is your old list the second Param is the initial value of each thread (I just initialized to datetime min). the third param block is as follows- the l is the same as in your code; the state is a Paralleloption object of which you can exit out of parallel loop if you choose; the last is the stand in variable that represents the thread local variable. The finalresult param represents the end result of each thread local variable and is called for each thread - it is there you can place a lock of the newList and add to the newList shared variable. In theory this works. I have used similar coding in my own code. Hope this helps you or someone else.




回答3:


As everyone has mentioned, there seems to be no case for doing this parallel. It will certainly be far, far slower. However, for completion, the reason this sometimes fails is there is no lock on the list object that's being written to by multiple threads. Add this:

object _locker = new object();
List<DateTime> newList = new List<DateTime>();
Parallel.ForEach(list, l => lock (_locker) newlist.Add(new DateTime(l.Ticks + 5000)));



回答4:


There simply is not enough work to do for this to warrant using Parallel.ForEach and also List<T> is not thread safe, so you would have to lock if you wanted to add to the same list in parallel. Just use a regular for loop.




回答5:


This will effectively call List<T>.Add concurrently, yet according to MSDN documentation for List<T>:

"Any instance members are not guaranteed to be thread safe."

Even if it were (thread safe), this is far too cheap to benefit from parallel execution (as opposed to overhead of parallel execution). Did you actually measure your performance? 25000 elements is not that many.




回答6:


Do you really need these in a list? If all you need is to enumerate the list in a foreach, you should probably do this instead, as it will use far less memory:

IEnumerable<DateTime> newSequence = list.Select(d => new DateTime(d.Ticks + 5000));

If you really need these in a list, just add .ToList() at the end:

var newSequence = list.Select(d => new DateTime(d.Ticks + 5000)).ToList();

This will almost certainly be fast enough that you don't need to parallelize it. In fact, this is probably faster than doing it in parallel, as it will have better memory performance.



来源:https://stackoverflow.com/questions/10149395/can-we-add-new-elements-to-a-list-using-parallel-foreach

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!