Gero
Gero

Reputation: 13553

multiple threads providing data to List<List<string>> to mainthread; multithreading

I have multiple threads that create a public List<List<String>> listOfLists and fill it with data. After all threads are done, I would like to use list.AddRange() to combine all those lists from the multiple threads in the listOfLits of the main thread.

So, if I have three threads and listOfLists of each thread has 10 elements, then listOflists of the main thread would have 30 elements. Just simple add 'em up and the mainthread object has to have all the data inserted by the threads.

It works perfectly if I use the static keyword for the listOfLists. All I want to do is to get rid of static.

Here is a little example.

  1. I create a simple Person object (main thread)
  2. main thread calls a method to create 3 threads
  3. each of those threads calls another method to fill its own listOfLists with data
  4. at the end of that method I add the threads own listOfLists to the static listOfLists
  5. and that is what I want to avoid. There must not be any static.
  6. I just want to have those thread data available in my mainthread and to proceed with the application on the main thread.

Edit: it has to be .NET 3.5

Edit2: One solution seems to be to provide the reference of listOfLists (of main thread) as parameter when starting the threads. Are there any other ways to solve such a threadingproblem?


namespace Threading
{
    class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person("mainThread");
            p.startMultiThreading();
        }                   
 }

class Person
{
    public String name;
    public List<String> privatePet;
    public List<List<String>> listOfLists = new List<List<string>>();
    public static List<List<String>> familyPets = new List<List<string>>();

    public Person(String n) { name = n; } //constructor
    public Person() { }

    public void startMultiThreading()
    {
        List<Thread> list_threads = new List<Thread>();
        for (int i = 0; i < 3; i++)
        {
            Person multiThreadPerson = new Person("multi: " + i); //create new Person
            Thread t = new Thread(multiThreadPerson.fill_List); //create new Thread
            t.Name = multiThreadPerson.name; //name the Thread with PersonName

            list_threads.Add(t);
            list_threads[i].Start(); //new Person is calling fill_list()
        }

        for (int i = 0; i < list_threads.Count; i++)
        {
            list_threads[i].Join();
        }

        familyPets.Add(new List<string>{"this is mainthread again"});
        foreach (List<String> list in familyPets)
        {
            list.ForEach(e => Debug.WriteLine(e));
        }
    }

    public void fill_List()
    {
        privatePet = new List<string>();
        lock (familyPets)
        {
            for (int i = 0; i < 20; i++)
            {
                privatePet.Add("dog :" + Thread.CurrentThread.Name);
                privatePet.Add("cat :" + this.name);
                listOfLists.Add(privatePet);
            }
            familyPets.AddRange(listOfLists); //adding up all listOfLists to the mainthread listOfLists
        }//lock 
    }
}

Upvotes: 1

Views: 2197

Answers (3)

abrkn
abrkn

Reputation: 2061

Enumerable.Range(0, 3).AsParallel().WithDegreeOfParallelism(3).Select(n => {
    var list = new List<string>();
    list.Add(n + ": One");
    list.Add(n + ": Two");
    list.Add(n + ": Three");
    return list;
}).ToList();

Or to combine the lists:

Enumerable.Range(0, 3).AsParallel().WithDegreeOfParallelism(3).Select(n => {
    var list = new List<string>();
    list.Add(n + ": One");
    list.Add(n + ": Two");
    list.Add(n + ": Three");
    return list;
}).Aggregate((a, b) => a.Union(b).ToList());

Read up on Tasks and Parallel on MSDN.

Upvotes: 0

Jon Hanna
Jon Hanna

Reputation: 113272

As well as the ThreadStart delegate, you can create a thread using the ParameterizedThreadStart delegate, and then Start() with an overload that takes a single object parameter. You make that object with whatever information you need to pass into the thread's delgate, which in this case is just the single list of lists that you are concerned with.

Note also that your locking was so wide as to only let one such thread to do anything, defeating the multi-threading. You want them to work on their own with things only they can deal with (privatePet in this case for example, and also i because each thread has their own separate views of them. Thread.CurrentThread is static but thread-safe (it would be pointless otherwise) and while this is not thread-safe and is shared, to have several threads read from a string field while zero threads are writing to it, is safe (you could make it safer still by making name readonly as then you could know with 100% confidence just by looking at that code that nothing was going to write to it, unless it went out of its way to mess with things.

public void fill_List(object targetList)
{
    List<List<String>> familyPets = (List<List<String>>)targetList;
    List<List<String>> listOfLists = new List<List<String>>();//we'll use our own local one of these thanks, as it's just this thread's concern write now.
    privatePet = new List<string>();
    for (int i = 0; i < 20; i++)
    {
        privatePet.Add("dog :" + Thread.CurrentThread.Name);
        privatePet.Add("cat :" + this.name);
        listOfLists.Add(privatePet);
    }
    //note you don't need to lock until you are doing something
    //that hits on something that other threads might hit on.
    //otherwise you've just got each thread locked for their entire
    //duration and you might as well just do the whole thing
    //in one thread.
    lock (familyPets)
    {
        familyPets.AddRange(listOfLists); //adding up all listOfLists to the mainthread listOfLists
    } 
}

Then in the calling method, you set up your list and pass it in the start:

list_threads[i].Start(whateverTheListIsCalled);

Upvotes: 1

StackUnder
StackUnder

Reputation: 252

I think that the problem is that you are using the same class "Person" for both filling data and joining data.

Person p = new Person("mainThread");

doesnt ever calls fill_List() and doesnt use the variables privatePet and listOfLists.

Without modifying your code too much, one easy solution would be to add a new constructor:

public Person(String n, List<List<String>> pRefFamilyPets) { name = n; familyPets = pRefFamilyPets; } //constructor

and overwrite this line, adding the familyPets reference

Person multiThreadPerson = new Person("multi: " + i, familyPets); //create new Person

I couldnt follow the logic but this seems to avoid the use of STATIC, since youre passing the reference around to the other threads.

Upvotes: 0

Related Questions