thread communication

R

Ronny

I have Winfrom application which invokes a working thread in the
background.
I'm looking for a simple tutorial/article regarding thread communication in
order to deal with the communication between them.
Can someone advise please?
Regards
Ronny
 
R

Ronny

Thanks Mr. Pete,
In my scenario the main thread should pass to the worker thread some string
from time to time. In C++ programming I would use global data as the
simplest mechanism (though not so elegant)but in C# I guise its forbidden.
Can you farther help?
Regards
Ronny
 
P

Peter Morris

You could quite easily create a class which has

A: A queue<string>
B: A worker thread
C: An object you use to lock()

When you want to add data you call a public method which does

lock(SyncRoot)
Queue.Enqueue(data);


When the thread wants data

string data = null;
string hasData = false;
lock(SyncRoot)
{
hasData = Queue.Count > 0;
if (hasData)
data = Queue.Dequeue();
}

//Process "data"

You can either make the thread sleep, or you can use an AutoResetEvent that
the thread waits for, and call AutoResetEvent.Set() from your public method
which enqueues.
 
P

Peter Morris

Hi Pete

http://msdn.microsoft.com/en-us/library/system.threading.monitor.pulse.aspx

I don't know if it's just too early in the morning or what, but this doesn't
make sense to me :)


public void FirstThread()
{
int counter = 0;
lock(m_smplQueue)
{
while(counter < MAX_LOOP_TIME)
{
// Release the lock and then regain it, so why lock in
the first place?
Monitor.Wait(m_smplQueue);
//Push one element.
m_smplQueue.Enqueue(counter);

//Let some other thread lock it?
Monitor.Pulse(m_smplQueue);

counter++;
}
}
}
public void SecondThread()
{
lock(m_smplQueue)
{
//Release the waiting thread.
Monitor.Pulse(m_smplQueue);
//Wait in the loop, while the queue is busy.
//Exit on the time-out when the first thread stops.
while(Monitor.Wait(m_smplQueue,1000))
{
//Pop the first element.
int counter = (int)m_smplQueue.Dequeue();
//Print the first element.
Console.WriteLine(counter.ToString());
//Release the waiting thread.
Monitor.Pulse(m_smplQueue);
}
}
}


Nah, it means nothing to me. The way it is explained just doesn't fit into
my brain, I suspect I have a misunderstanding of something else somewhere
which prevents it from making sense. Would you have time to explain it to
me please?
 
P

Peter Morris

Hi Pete
Anyway, that's a long way of saying that the MSDN sample is unnecessarily
confusing. I'm not surprised it didn't help you. I'm not sure how it'd
help anyone who didn't already know how to use the class.

I am glad, because as I read it I had no idea what it was trying to teach me
:)

void ProcessLoop()
void AddWork(object obj)
<snip>

Now that example I looked at and understood immediately :)

In summary

Monitor.Wait(SyncRoot);
will do this
01: Release the lock
02: Suspend the thread until Monitor.Pulse(SyncRoot) is called

Monitor.Pulse(SyncRoot);
will do this
01: Release the lock
02: Awaken any threads suspended using Monitor.Wait(SyncRoot)

I presume it is a mistake to do this...

Monitor.Wait(SyncRoot);
//Some other code here that depends on the lock being held

and finally, if this were a service and I wanted to let the worker thread
die I would send a Pulse to wake it up so that it had the opportunity to
check that the service has been stopped?

An excellent example, MSDN should be updated :) I really appreciate you
spending your time explaining this to me, thanks!
 
P

Peter Morris

Not quite. Calling Pulse() doesn't release the lock. Exiting the lock()
statement does

Ah yes ofcourse. I was remembering the code rather than reading it. The
Pulse() isn't in a loop, so after sending Pulse() it drops out of the lock.


Glad I asked. Although Monitor.Wait() releases the lock the thread isn't
resumed until 2 conditions are met.
01: Pulse
02: It re-acquires the lock

Excellent. Very well taught :)

I will post feedback on the MSDN article with a link to your source.

Thanks again!
 
P

Peter Morris

Hi Pete

This morning I changed some code. I previously had a Queue<IFileProcessor>
which I checked in a thread and called Execute() on each instance as I
removed it. I changed this into a simple class which has a SyncRoot which I
can Wait and Pulse, and then some code to process the queue.

Now I have 3 queues

Dictionary<EstimatedProcessTime, Queue<IFileProcessor>> Queues;

Previously I did something like this

while (true)
{
bool hasWork;

//These take milliseconds, so we do all of these first
Dequeue(Queues[EstimatedProcessTime.Immediate], out hasWork);
if (hasWork)
continue;

//These take about 1 second, so do these when the Immediate ones are
done
Dequeue(Queues[EstimatedProcessTime.Medium], out hasWork);
if (hasWork)
continue;

//These take about 12 seconds, so only do when then others are done
Dequeue(Queues[EstimatedProcessTime.Long], out hasWork);

Thread.Sleep(1000);
}

private void Dequeue(Queue<IFileProcessor> queue, out bool hasWork)
{
IFileProcessor processor = null;
lock(queue)
{
if (queue.Count > 0)
processor = queue.Dequeue();
hasWork = (queue.Count > 0);
}
processor.Execute();
}

The problem with this is that the 12 second process would stop the
immediate/medium length processes from executing. I have intended to make
this 1 thread per queue for a few days now, so after our discussion I
changed it this morning...

public class FileProcessorQueue
{
public readonly object SyncRoot;
readonly Thread Thread;
readonly Queue<IFileProcessor> Queue;

public FileProcessorQueue()
{
SyncRoot = new object();
Queue = new Queue<IFileProcessor>();
Thread = new Thread(new ThreadStart(ProcessQueue));
Thread.Start();
}

public void Enqueue(IFileProcessor processor)
{
lock (SyncRoot)
{
Queue.Enqueue(processor);
Monitor.Pulse(SyncRoot);
}
}

private void ProcessQueue()
{
while (true)
{
IFileProcessor processor = null;
lock (SyncRoot)
{
if (Queue.Count > 0)
processor = Queue.Dequeue();
else
Monitor.Wait(SyncRoot);
}//lock syncroot
if (processor != null)
{
processor.Execute();
if (processor.State == FileProcessState.Ready)
Enqueue(processor);
}
}//while true
}


public void Clear()
{
lock (SyncRoot)
{
Queue.Clear();
}
}
}


Much happier now :)
 
M

Michael J. Ryan

Thanks Chris,
Looks nice but I miss the dual way communication. In the main thread to
deliver paramters and data to the worker thread- how can I do that?
Regards

For getting communications out of a worker thread, you should have events that
are subscribed to by the main thread, especially if this means UI updates.
Have your worker thread simply execute the event. Your main class just
subscribes to the event... myObj.WorkComplete += ...;

If your worker thread is attached to a given class, getting things *TO* that
worker class is easy (example below). Just expose a method to add work to the
queue... Hope this helps you.

example:
public class WorkerClass>
{
public delegate void WorkHandler(WorkItem work);
public event WorkHandler WorkComplete;

private object _hnd = new Object();
private Thread _worker;
private System.Collections.Generic.Queue<WorkItem> _queue =
new System.Collections.Generic.Queue<WorkItem>();

public void Start() {
lock (_hnd) {
Stop();
_worker = new Thread(Worker);
_worker.start();
}
}

public void Stop() {
lock (_hnd) {
if (_worker != null && _worker.IsAlive) {
_worker.Abort();
}
}
}

private void Worker() {
WorkItem work;
bool working;
try {
while (true) {
work = null;
lock(_queue) {
if (_queue.Count > 0)
work = _queue.Dequeue();
}
if (work == null) {
System.Threading.Thread.Sleep(100);
}

// DO WORK
working = true;
work.DoWork();
workingn = false;

WorkComplete(work);
}
} catch (System.Threading.ThreadAbortException) {
//do nothing, stop has been called on this thread.
if (working)
AddWork(work); //requeue work item.
}
}

public void AddWork(WorkItem work) {
lock(_queue) {
_queue.Enqueue(work);
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top