I need to create 5 threads and associate an ArrayList with each thread .I have another thread which will read values from a queue (one by one) and push that message to the
ArrayList associated with each thread which i created earlier . Then that thread should read value from the ArrayList and start executing. How can I do that?
-
1Why are you using an ArrayList and not a Queue<T> ?Joel Coehoorn– Joel Coehoorn2011-12-12 05:50:00 +00:00Commented Dec 12, 2011 at 5:50
-
For most cases it's far better to let the thread pool manage the number of active threads. I would propose reconsidering your approach, it's rare that creating a fixed number of explicit threads is the best approach.Adam Ralph– Adam Ralph2011-12-12 08:00:28 +00:00Commented Dec 12, 2011 at 8:00
3 Answers
You can use the Monitor class to synchronize your threads. Here is an example using five different locks and queues, one for each thread, based on your comments.
It's important to protect with a lock any data shared between two different threads. Only the thread holding the lock should access the data.
The worker thread locks on its own personal object (in the syncs array), and, having the lock, then calls Monitor.Wait, which will release the lock.
The main thread may have already tried to lock that thread's sync object, or soon will, it doesn't matter because it won't have access to that thread's queue until it does have the lock. Then it is safe to queue up a message. The Monitor.Pulse call wakes up the waiting worker thread, but the worker stays stuck in its Monitor.Wait call until the main thread releases the lock (by falling out of the lock() { } code block.)
When Monitor.Wait returns to the worker, the lock will have been re-acquired.
You can't always assume that the threads will pulse and wake at the same rate, which is why I have the extra while loop in the worker thread, to handle the case where the main thread has signaled a few times with several messages before the worker actually woke up to process them.
This example is much simplified -- it doesn't cover shutting the workers down, for example, but it should give you some ideas get started.
namespace ConsoleApplication1
{
using System;
using System.Collections.Generic;
using System.Threading;
internal class Program
{
private static readonly Queue<int>[] queues = new Queue<int>[5];
private static readonly object[] syncs = new object[5];
public static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
queues[i] = new Queue<int>();
syncs[i] = new object();
var thread = new Thread(ThreadProc);
thread.Start(i);
}
var random = new Random();
while (true)
{
Thread.Sleep(1000);
int index = random.Next(queues.Length);
lock (syncs[index])
{
int message = random.Next(100);
queues[index].Enqueue(message);
Console.WriteLine("Sending message " + message + " to thread at " + index);
Monitor.Pulse(syncs[index]);
}
}
}
private static void ThreadProc(object data)
{
var index = (int)data;
lock (syncs[index])
{
while (true)
{
while (queues[index].Count == 0)
{
Monitor.Wait(syncs[index]);
}
int message = queues[index].Dequeue();
Console.WriteLine("Thread at " + index + " received message " + message);
}
}
}
}
}
1 Comment
You cannot use array lists without synchronization, because the parceling thread (thread #6) will be writing to the places from which the other five would be reading. Using BlockingCollection is a good choice for your task.
Create and initialize an array of five BlockingCollection<T> objects, and pass each thread the index of its collection in the array through the parameter object. Each of the five "worker" threads should loop on the call to Take() on the blocking collection at the index passed at initialization, and do whatever they need to do. The work parceling thread should use Add to add values designated for each thread.
Comments
I know this doesn't directly answer what you're asking, but I'm just wondering if this idea might better fit what you're trying to do.
Rather than have an ArrayList for each thread, make the main queue a thread-safe queue System.Collections.Concurrent.ConcurrentQueue, and give each thread a reference to it. Then you don't need the other thread to hand-off the work to your worker threads.
Each worker thread can just check the queue to see if there is any work waiting. If so it grabs it and does it's processing. If not, the thread sleeps for a period before checking again.
This won't work well if specific threads need to handle specific types of values (and that's what your other thread is managing), but it should be reasonable for basic load sharing across a pool of workers.