I'm trying to populate a list with a million independently generated items. But after the loop I'm getting less items: 999960, 998534 etc.
As a bypass I've wrapped this code in another that checks generated amount and generates missing items.
I've also tried to sleep after the loop, but it doesn't give a result closer to desired.
var someList = new List<ListItem>();
Parallel.For(0, 1000000, _ =>
{
var item = new ListItem();
//some logic here
someList.Add(item);
});
System.Console.WriteLine(someList.Count()); // returns less than 1000000
What is the reason of such behaviour and what is the proper way to solve this task?
CodePudding user response:
If you know how many items you will generate, just use an array:
var someList = new ListItem[1000000];
Parallel.For(0, someList.Length, i =>
{
var item = new ListItem();
//some logic here
someList[i] = item;
});
CodePudding user response:
The List<T>
is not thread-safe. You can use a ConcurrentBag<T>
, although that is an ICollection<T>
, not an IList<T>
. If you need the latter then you will have to synchronise the multiple threads, probably using a lock
statement.
CodePudding user response:
When you want the results of a parallel operation, the PLINQ is more convenient than the Parallel
class:
List<ListItem> results = Enumerable.Range(0, 1_000_000)
.AsParallel()
.Select(_ =>
{
ListItem item = new();
// Some logic here
return item;
})
.ToList();
A few differences that you should have in mind:
- The
Parallel
runs the code on theThreadPool
by default, but it's configurable. The PLINQ uses exclusively theThreadPool
. - The
Parallel
by default has unlimited parallelism (it uses all the available threads of theThreadPool
). The PLINQ uses by default at mostEnvironment.ProcessorCount
threads.
The PLINQ by default doesn't preserve the order of the results. You can configure it with the AsOrdered
if you want.