How to add to a List while using Multi-Threading?
Asked Answered
R

4

8

I'm kinda new to Multi-Threading and have only played around with it in the past. But I'm curious if it is possible to have a List of byte arrays on a main thread and still be able to add to that List while creating the new byte array in a seperate Thread. Also, I'll be using a for-each loop that will go through a list of forms that will be used to parse into the byte array. So basically a pseudo code would be like this...

reports = new List();

foreach (form in forms)  
{  
    newReport = new Thread(ParseForm(form));  
    reports.Add(newReport);  

}  

void ParseForm(form)  
{  
    newArray = new byte[];  
    newArray = Convert.ToBytes(form);  
    return newArray;  
}

Hopefully the pseudo-code above makes some sense. If anyone could tell me if this is possible and point me in the direction of a good example, I'm sure I can figure out the actual code.

Rosefish answered 16/5, 2012 at 14:8 Comment(1)
Forgot to mention, the webservice is using .Net 3.5 so I don't believe Tasks are available.Rosefish
E
4

If you need to access a collection from multiple threads, you should either use synchronization, or use a SynchronizedCollection if your .NET version is 3.0 or higher.

Here is one way to make the collection accessible to your thread:

SynchronizedCollection reports = new SynchronizedCollection();

foreach (form in forms) {  
    var reportThread = new Thread(() => ParseForm(form, reports));
    reportThread.Start();
}

void ParseForm(Form form, SynchronizedCollection reports) {  
    newArray = new byte[];  
    newArray = Convert.ToBytes(form);  
    reports.Add(newArray);
}

If you are on .NET 4 or later, a much better alternative to managing your threads manually is presented by various classes of the System.Threading.Tasks namespace. Consider exploring this alternative before deciding on your threading implementation.

Epos answered 16/5, 2012 at 14:17 Comment(4)
Thanks, I'll have to try the SynchronizedCollection since I'm still on 3.5.Rosefish
Do I need to set a reference for the SynchronizedCollection? I have the using statements for Collections.Genereric and Threading. And I checked that it is using 3.5, but I'm not getting a SynchronizedCollection.Rosefish
@Rosefish You need to add a reference to System.ServiceModel.Epos
Thanks, that got it regocnized. Let me see if this will works.Rosefish
Z
5

In before we realized it was .Net 3.5, keep for reference on .Net 4

If you don't need any order within the list, an easy "fix" is to use the ConcurrentBag<T> class instead of a list. If you need more order, there is also a ConcurrentQueue<T> collection too.

If you really need something more custom, you can implement your own blocking collection using BlockingCollection<T>. Here's a good article on the topic.

You can also use Parallel.Foreach to avoid the explicit thread creation too:

private void ParseForms()
{
    var reports = new ConcurrentBag<byte[]>();
    Parallel.ForEach(forms, (form) =>
                                {
                                    reports.Add(ParseForm(form));
                                });

}

private byte[] ParseForm(form)  
{  
    newArray = new byte[];  
    newArray = Convert.ToBytes(form);  
    return newArray;  
}
Ziwot answered 16/5, 2012 at 14:15 Comment(4)
Is the ConcurrentBag just 4.0 and higher?Rosefish
Yes, just saw your note too. This is ridiculously easy in .Net 4.0 if you can use it.Ziwot
I have to agree with you there. I'm not sure why they haven't upgraded this service to 4.0 yet. Everything else is using it.Rosefish
Otherwise you will have to use a Threadpool and write your own blocking collection. It is completely worth it to upgrade to .Net 4.0 and use the Task Parallel Libraries, as it is very hard to get it correct for us mere single threaded mortals.Ziwot
E
4

If you need to access a collection from multiple threads, you should either use synchronization, or use a SynchronizedCollection if your .NET version is 3.0 or higher.

Here is one way to make the collection accessible to your thread:

SynchronizedCollection reports = new SynchronizedCollection();

foreach (form in forms) {  
    var reportThread = new Thread(() => ParseForm(form, reports));
    reportThread.Start();
}

void ParseForm(Form form, SynchronizedCollection reports) {  
    newArray = new byte[];  
    newArray = Convert.ToBytes(form);  
    reports.Add(newArray);
}

If you are on .NET 4 or later, a much better alternative to managing your threads manually is presented by various classes of the System.Threading.Tasks namespace. Consider exploring this alternative before deciding on your threading implementation.

Epos answered 16/5, 2012 at 14:17 Comment(4)
Thanks, I'll have to try the SynchronizedCollection since I'm still on 3.5.Rosefish
Do I need to set a reference for the SynchronizedCollection? I have the using statements for Collections.Genereric and Threading. And I checked that it is using 3.5, but I'm not getting a SynchronizedCollection.Rosefish
@Rosefish You need to add a reference to System.ServiceModel.Epos
Thanks, that got it regocnized. Let me see if this will works.Rosefish
C
0

Why is enumerate files returning the same file more than once?

Check that out. It shows I think exactly what you want to do.

It creates a list on the main thread then adds to it from a different thread. your going to need

using System.Threading.Tasks

-

Files.Clear(); //List<string> 

Task.Factory.StartNew( () =>
{
      this.BeginInvoke( new Action(() =>
         {
            Files.Add("Hi");
         }));
 });
Casablanca answered 16/5, 2012 at 14:12 Comment(1)
It works only if this is a System.Windows.Forms.Control (or derived classes). It's not a synchronization mechanism but a way to interact with UI from other threads.Dotty
A
0

Below is a simple Blocking Collection (as a queue only) that I just whipped up now since you don't have access to C# 4.0. It's most likely less efficient than the 4.0 concurrent collections, but it should work well enough. I didn't re-implement all of the Queue methods, just enqueue, dequeue, and peek. If you need others and can't figure out how they would be implemented just mention it in the comments.

Once you have the working blocking collection you can simply add to it from the producer threads and remove from it using the consumer threads.

public class MyBlockingQueue<T>
{
    private Queue<T> queue = new Queue<T>();
    private AutoResetEvent signal = new AutoResetEvent(false);
    private object padLock = new object();

    public void Enqueue(T item)
    {
        lock (padLock)
        {
            queue.Enqueue(item);
            signal.Set();
        }
    }

    public T Peek()
    {
        lock (padLock)
        {
            while (queue.Count < 1)
            {
                signal.WaitOne();
            }

            return queue.Peek();
        }
    }

    public T Dequeue()
    {
        lock (padLock)
        {
            while (queue.Count < 1)
            {
                signal.WaitOne();
            }

            return queue.Dequeue();
        }
    }
}
Aftergrowth answered 16/5, 2012 at 14:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.