By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
458,187 Members | 1,629 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 458,187 IT Pros & Developers. It's quick & easy.

Re: thread communication

P: n/a
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
Ronny
Take a look at the background worker thread component. It has what
you want built into it.

http://msdn.microsoft.com/en-us/library/8xs8549b.aspx

Chris
Oct 13 '08 #1
Share this Question
Share on Google+
12 Replies


P: n/a
On Mon, 13 Oct 2008 10:19:08 -0700, Ronny <ro***@john.comwrote:
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?
It really depends on the exact data, what the thread will do with it, and
how you want the data to be delivered.

The simplest approach is to pass all the data to the thread when you start
it up. The BackgroundWorker class supports this easily. To pass data to
a thread as it's executing means you need to come up with a specific
strategy of what data is being passed, when the thread will retrieve it,
and how. One possible approach is to use a queue that the worker thread
reads from while other threads write to (see "producer/consumer" in
Google). But that's hardly the only approach one might take, nor is it
universally the most appropriate.

Unless you can provide a more specific question, it's going to be very
difficult to provide any sort of answer to your question.

Pete
Oct 13 '08 #2

P: n/a
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
"Peter Duniho" <Np*********@nnowslpianmk.comwrote in message
news:op***************@petes-computer.local...
On Mon, 13 Oct 2008 10:19:08 -0700, Ronny <ro***@john.comwrote:
>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?

It really depends on the exact data, what the thread will do with it, and
how you want the data to be delivered.

The simplest approach is to pass all the data to the thread when you start
it up. The BackgroundWorker class supports this easily. To pass data to
a thread as it's executing means you need to come up with a specific
strategy of what data is being passed, when the thread will retrieve it,
and how. One possible approach is to use a queue that the worker thread
reads from while other threads write to (see "producer/consumer" in
Google). But that's hardly the only approach one might take, nor is it
universally the most appropriate.

Unless you can provide a more specific question, it's going to be very
difficult to provide any sort of answer to your question.

Pete

Oct 13 '08 #3

P: n/a
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.

--
Pete
====
http://mrpmorris.blogspot.com
http://www.capableobjects.com

Oct 13 '08 #4

P: n/a
On Mon, 13 Oct 2008 14:07:24 -0700, Peter Morris
<mr*********@spamgmail.comwrote:
[...]
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.
Though, IMHO for this pattern, using the Monitor class is more idiomatic
for C#/.NET. The consumer thread can just call Monitor.Wait() from within
the lock, while the producer thread will call Monitor.Pulse() when new
data is added to the queue.

Of course, all of the preceding (Peter M.'s pseudocode as well as my
elaboration) assumes a worker thread that _only_ processes data sent to it
via the queue. IME, this is a very common scenario, but if you the
original poster have a thread that is doing other work as well, you will
have to figure out how to decide if and when to check the queue for new
data.

To the OP, re: your previous comment about "In C++ programming I would use
global data as the simplest mechanism (though not so elegant)but in C# I
guise its forbidden", while it's true there's not really any such thing as
global data in C#, if you really wanted to, you could implement a solution
that uses practically the same technique, simply by using a variable
within a class to hold the data.

In fact, to some extent, that's what the queue does, just in a more
flexible way (it can hold more than one item at a time). The variable can
be a static member of a class, making it _very_ similar to your global
variable in C++, or it can be an instance member of a class, allowing you
to implement the functionality in a way that provides for multiple
instances of the class to be operating independently simultaneously.

I do think that a queue would be more elegant than a single variable, but
if you know you only ever have one piece of data to transfer at a time,
the queue is overkill. :)

Pete
Oct 13 '08 #5

P: n/a
Hi Pete

http://msdn.microsoft.com/en-us/libr...tor.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?
--
Pete
====
http://mrpmorris.blogspot.com
http://www.capableobjects.com

Oct 14 '08 #6

P: n/a
On Tue, 14 Oct 2008 00:39:37 -0700, Peter Morris
<mr*********@spamgmail.comwrote:
[...]
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?
I'm not sure what it is about Microsoft tech writers that motivate them to
write samples like that. I've seen similar stuff elsewhere (for example,
the sample for the Interlocked class), where they take what should be a
simple concept and make up some complicated, contrived example.

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.

A more typical use of the Monitor class would be to have one thread using
Wait() and the other using Pulse(). For example (warning: more uncompiled
code ahead :) ):

object _objLock = new object();
Queue<object_queue = new Queue<object>();

void ProcessLoop()
{
lock (_objLock)
{
while (true)
{
if (_queue.Count 0)
{
object obj = _queue.Dequeue();

// process "obj" somehow
}
else
{
Monitor.Wait(_objLock);
}
}
}
}

void AddWork(object obj)
{
lock (_objLock)
{
_queue.Enqueue(obj);
Monitor.Pulse(_objLock);
}
}

Basically:

-- for both Wait() and Pulse(), the calling thread must already own
the lock
-- Wait() will _release_ the lock and suspend the thread until some
other thread calls Pulse(); the suspended thread is moved into a wait
queue for the lock
-- Pulse() will release _a_ thread waiting on the lock, by moving it
into a ready queue for the lock; when the calling thread releases the
actual lock, then the released thread will then acquire the lock and be
able to run when it's next scheduled

Note that multiple threads could actually be waiting on the lock. Pulse()
just picks the first one in the wait queue and moves it to the ready
queue. Likewise, when a thread releases the lock, the first one in the
ready queue is the one that acquires the lock next. If you have only one
consumer thread, then obviously that thread is always the one that gets to
resume when Pulse() is called.

The doc pages for those methods have various comments and caveats. The
Monitor class isn't _always_ the right approach, but it does fit into the
producer/consumer scenario very well, and is very useful in other patterns
too.

Pete
Oct 14 '08 #7

P: n/a
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()
<snip>
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!

--
Pete
====
http://mrpmorris.blogspot.com
http://www.capableobjects.com

Oct 14 '08 #8

P: n/a
On Tue, 14 Oct 2008 04:02:25 -0700, Peter Morris
<mr*********@spamgmail.comwrote:
[...]
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)
Not quite. Calling Pulse() doesn't release the lock. Exiting the lock()
statement or calling Monitor.Leave() does (they are equivalent). So all
that calling Pulse() does is your "02" item, and only in the sense that a
suspended thread gets moved into the ready queue for the Monitor object.
That thread still won't be runnable until two conditions are met: 1) it's
the first thread in the ready queue, and 2) no other thread still has the
lock.

The first condition might wind up being met as a direct result of Pulse()
being called, but the second can't be met until the thread that called
Pulse() releases the lock.
I presume it is a mistake to do this...

Monitor.Wait(SyncRoot);
//Some other code here that depends on the lock being held
Nope, that's fine (and in fact, the code I posted does basically
that...see the while(true) loop). In particular, the Wait() method may
not be called unless the calling thread has acquired the lock. And when
Wait() returns, the calling thread will have re-acquired the lock (having
released it while it was waiting).
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?
Yes. For example, if you had a queue managed by the code I posted, one
strategy for telling the code to exit the while(true) loop would be to
enqueue a special sentinel value that signals the ProcessLoop() method to
break out of the loop.
An excellent example, MSDN should be updated :-) I really appreciate
you spending your time explaining this to me, thanks!
No problem, happy to help. :)

Pete
Oct 14 '08 #9

P: n/a
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.

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

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

Nope,
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!
--
Pete
====
http://mrpmorris.blogspot.com
http://www.capableobjects.com

Oct 14 '08 #10

P: n/a
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<IFileProcessorqueue, 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<IFileProcessorQueue;

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 :-)

--
Pete
====
http://mrpmorris.blogspot.com
http://www.capableobjects.com

Oct 15 '08 #11

P: n/a
On Wed, 15 Oct 2008 00:12:52 -0700, Peter Morris
<mr*********@spamgmail.comwrote:
Hi Pete

This morning I changed some code. I previously had a
Queue<IFileProcessorwhich 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.

[...]

Much happier now :-)
Cool...glad the info helped. The new version looks good.

One thing I noticed in your code though, in your desire to move the actual
processing outside of the lock (a good idea), you have introduced an
inefficiency in the synchronization (not so good).

Specifically, in the scenario in which your thread is blocked waiting, if
something is enqueued, the very first thing that the thread does after
being woken up again is to release the lock and then try to acquire it
again. That's inefficient in and of itself, but it gets really bad if
there's another thread also trying to get the lock at the same time. The
thread you just woke up then has to go right back to the waiting state
until the lock gets released again.

On a single-CPU system this winds up being not _quite_ so bad, because
there's a good chance your thread will get back to the reacquisition of
the lock before it uses up its quantum (in fact, in the code you posted,
it's practically guaranteed). But on a multi-core system, it's entirely
possible that even in that short period of time, some other thread can
grab the lock.

If these tasks don't come up very often, that's probably okay. The
inefficiency won't matter. But in a high-throughput system, lock
contention and context switching can be expensive.

If you think the inefficiency might actually be an issue, there are surely
even higher-performance implementations a true threading expert could come
up with, but at the very least, you might want to consider something more
like this:

private void ProcessQueue()
{
lock (SyncRoot)
{
while (true)
{
if (Queue.Count 0)
{
IFileProcessor processor = Queue.Dequeue();

Monitor.Leave(SyncRoot);
processor.Execute();
Monitor.Enter(SyncRoot);

if (processor.State == FileProcessState.Ready)
{
Enqueue(processor);
}
}
else
{
Monitor.Wait(SyncRoot);
}
}
}
}

This allows the thread to retain the lock all the way from being woken up
until it goes to check the queue again.

You may be able to gain even more efficiency by emptying the queue all at
once before releasing the lock, putting each dequeued IFileProcessor
instance into a temporary array. That will tend to minimize the number of
times you actually have to release and then reacquire the lock. (Though,
again...this would only matter in a high-throughput situation). For
example, changing the above if() and true clause to:

if (Queue.Count 0)
{
IFileProcessor[] processors = Queue.ToArray();
List<IFileProcessorreadyList = new
List<IFileProcessor>(processors.Length);

Queue.Clear();

Monitor.Leave(SyncRoot);
foreach (IFileProcessor processor in processors)
{
processor.Execute();
if (processor.State == FileProcessState.Ready)
{
readyList.Add(processor);
}
}
Monitor.Enter(SyncRoot);

foreach (IFileProcessor processor in readyList)
{
Enqueue(processor);
}
}

That way, the thread grabs as much of the current state as it can before
letting go of the lock. It also queues all the new "ready" things it
knows about all at once.

Finally, if you want to get really picky, at least from the code you
posted it appears that pushing the new "ready" items back through the
queue is wasteful. Especially with the above alternative, you could
easily maintain a local Queue to which all of the public Queue elements
are copied, and then just loop while unlocked until the local Queue is
empty, adding the "ready" items back into the local Queue as you find
them. (Of course, you'd only want to do that if a) you can guarantee that
doing so won't starve new items being added to the public queue during the
processing of the local queue, or b) that starving those items won't
matter...it would essentially boost the priority of the items that remain
ready after processing above any newly added items that haven't been
processed yet).

Thanks for sharing...it's these practical "code in the wild" examples that
I think really help illustrate the different approaches available in C#
for specific tasks. There's nothing quite like a real-world case study.
:)

Pete
Oct 15 '08 #12

P: n/a
On 10/13/2008 10:19 AM, Ronny wrote:
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);
}
}
}

--
Michael J. Ryan - tracker1(at)theroughnecks(dot)net - www.theroughnecks.net
icq: 4935386 - AIM/AOL: azTracker1 - Y!: azTracker1 - MSN/Win: (email)

.... FRA #009: Opportunity plus instinct equals profit.
Oct 16 '08 #13

This discussion thread is closed

Replies have been disabled for this discussion.