You must start with the general Producer-Consumer lineup and use it. Implementing this inside a queue is not such a good idea, as it prevents you from using semaphores for signaling (or you can have open semaphores in your queue, but this is a bad idea indeed ).
Once thread A has inserted one work item, it should signal a semaphore to notify stream B. When thread B has finished processing all the elements, it should signal a semaphore to notify everyone else that it has finished. Your main thread should wait until this second semaphore finds out that everything is done.
[change]
First you have a manufacturer and a consumer:
public interface IProducer<T> : IStoppable { /// <summary> /// Notifies clients when a new item is produced. /// </summary> event EventHandler<ProducedItemEventArgs<T>> ItemProduced; } public interface IConsumer<T> : IStoppable { /// <summary> /// Performs processing of the specified item. /// </summary> /// <param name="item">The item.</param> void ConsumeItem(T item); } public interface IStoppable { void Stop(); }
So, in your case, the class that creates the mail will have to fire the ItemProduced
event, and to send the class it will need to implement ConsumeItem
.
And then you pass these two instances to the Worker
instance:
public class Worker<T> { private readonly Object _lock = new Object(); private readonly Queue<T> _queuedItems = new Queue<T>(); private readonly AutoResetEvent _itemReadyEvt = new AutoResetEvent(false); private readonly IProducer<T> _producer; private readonly IConsumer<T> _consumer; private volatile bool _ending = false; private Thread _workerThread; public Worker(IProducer<T> producer, IConsumer<T> consumer) { _producer = producer; _consumer = consumer; } public void Start(ThreadPriority priority) { _producer.ItemProduced += Producer_ItemProduced; _ending = false;
This general idea may require additional customization.
To use it, you need your classes to implement these two interfaces:
IProducer<IMail> mailCreator = new MailCreator(); IConsumer<IMail> mailSender = new MailSender(); Worker<IMail> worker = new Worker<IMail>(mailCreator, mailSender); worker.Start();
The great thing about this:
- you can have as many workers as possible
- multiple employees may accept items from the same manufacturer.
- multiple workers can send items to the same consumer (although this means that you need to accept the case where the consumer is implemented in a thread-safe manner)