Parallel.ForEach with adding to list

C#ListLockingParallel Processing

C# Problem Overview


I'm trying to run multiple functions that connect to a remote site (by network) and return a generic list. But I want to run them simultaneously.

For example:

public static List<SearchResult> Search(string title)
{
    //Initialize a new temp list to hold all search results
    List<SearchResult> results = new List<SearchResult>();

    //Loop all providers simultaneously
    Parallel.ForEach(Providers, currentProvider =>
    {
        List<SearchResult> tmpResults = currentProvider.SearchTitle((title));

        //Add results from current provider
        results.AddRange(tmpResults);
    });

    //Return all combined results
    return results;
}

As I see it, multiple insertions to 'results' may happend at the same time... Which may crash my application.

How can I avoid this?

C# Solutions


Solution 1 - C#

You can use a concurrent collection.

> The System.Collections.Concurrent namespace provides several thread-safe collection classes that should be used in place of the corresponding types in the System.Collections and System.Collections.Generic namespaces whenever multiple threads are accessing the collection concurrently.

You could for example use ConcurrentBag since you have no guarantee which order the items will be added.

> Represents a thread-safe, unordered collection of objects.

Solution 2 - C#

//In the class scope:
Object lockMe = new Object();    

//In the function
lock (lockMe)
{    
     results.AddRange(tmpResults);
}

Basically a lock means that only one thread can have access to that critical section at the same time.

Solution 3 - C#

For those who prefer code:

public static ConcurrentBag<SearchResult> Search(string title)
{
    var results = new ConcurrentBag<SearchResult>();
    Parallel.ForEach(Providers, currentProvider =>
    {
        results.Add(currentProvider.SearchTitle((title)));
    });

    return results;
}

Solution 4 - C#

The Concurrent Collections are new for .Net 4; they are designed to work with the new parallel functionality.

See Concurrent Collections in the .NET Framework 4:

> Before .NET 4, you had to provide your own synchronization mechanisms if multiple threads might be accessing a single shared collection. You had to lock the collection ... > > ... the [new] classes and interfaces in System.Collections.Concurrent [added in .NET 4] provide a consistent implementation for [...] multi-threaded programming problems involving shared data across threads.

Solution 5 - C#

This could be expressed concisely using PLINQ's AsParallel and SelectMany:

public static List<SearchResult> Search(string title)
{
    return Providers.AsParallel()
                    .SelectMany(p => p.SearchTitle(title))
                    .ToList();
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionshaharmorView Question on Stackoverflow
Solution 1 - C#Mark ByersView Answer on Stackoverflow
Solution 2 - C#HaedrianView Answer on Stackoverflow
Solution 3 - C#Matas VaitkeviciusView Answer on Stackoverflow
Solution 4 - C#Matt MillsView Answer on Stackoverflow
Solution 5 - C#DouglasView Answer on Stackoverflow