Rising events between threads

G

Gotch@

Hi all,

I've recently digged into C# and the whole .Net stuff. Particularly I
found the idea of adding Events and Delegates to the C# language very
interesting and I'm trying to use them in every weird way that comes
to my mind. Particularly I'm struggling to find a way to use events
between threads.

In particular I'd like to know how to rise and event from a thread and
have it received from the delegate in another thread (I mean the other
thread wakes up and executes the delegate)... And going over... is it
possible to broadcast events? Or, even better, to decide the
destination thread?

I remember that in the past there where messages in windows, not
events, and you could send them through threads safely. But they were
a different thing cos, I think, delegates are just function pointers
and events are bundles of them (ok, with some extras maybe)...

So is it possible to do this with events? Or are there other ways,
similarly easy and convenient (I'm not taling about Monitor, Mutex and
co... I know them...)?

Thanks in advance. Bye.
 
N

Nicholas Paldino [.NET/C# MVP]

You can not inject a call into the call stack of another thread without
that thread doing something to be notified of the incoming call. This is
why you can have delegates called on a UI thread. Because the UI thread has
a message pump that cycles in a loop, you can inject something to be
processed into that loop, in this case, your delegate call.

For other threads, you pretty much have to do the same thing, that is,
have a loop waiting for a signal to execute your delegate.
 
G

Gotch@

Thanks,
and my question is... How do I do that?


You can not inject a call into the call stack of another thread without
that thread doing something to be notified of the incoming call. This is
why you can have delegates called on a UI thread. Because the UI thread has
a message pump that cycles in a loop, you can inject something to be
processed into that loop, in this case, your delegate call.

For other threads, you pretty much have to do the same thing, that is,
have a loop waiting for a signal to execute your delegate.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)


I've recently digged into C# and the whole .Net stuff. Particularly I
found the idea of adding Events and Delegates to the C# language very
interesting and I'm trying to use them in every weird way that comes
to my mind. Particularly I'm struggling to find a way to use events
between threads.
In particular I'd like to know how to rise and event from a thread and
have it received from the delegate in another thread (I mean the other
thread wakes up and executes the delegate)... And going over... is it
possible to broadcast events? Or, even better, to decide the
destination thread?
I remember that in the past there where messages in windows, not
events, and you could send them through threads safely. But they were
a different thing cos, I think, delegates are just function pointers
and events are bundles of them (ok, with some extras maybe)...
So is it possible to do this with events? Or are there other ways,
similarly easy and convenient (I'm not taling about Monitor, Mutex and
co... I know them...)?
Thanks in advance. Bye.
 
N

Nicholas Paldino [.NET/C# MVP]

Just like I told you, you basically have to have a loop in your thread
which will process messages/notifications which are set on other threads.
You can use windows messages, some sort of queue structure, etc, etc. It's
a basic producer/consumer pattern.

Is there a particular reason why you want to have one single thread
process delegates? What exactly are you trying to do?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Gotch@ said:
Thanks,
and my question is... How do I do that?


You can not inject a call into the call stack of another thread
without
that thread doing something to be notified of the incoming call. This is
why you can have delegates called on a UI thread. Because the UI thread
has
a message pump that cycles in a loop, you can inject something to be
processed into that loop, in this case, your delegate call.

For other threads, you pretty much have to do the same thing, that
is,
have a loop waiting for a signal to execute your delegate.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)


I've recently digged into C# and the whole .Net stuff. Particularly I
found the idea of adding Events and Delegates to the C# language very
interesting and I'm trying to use them in every weird way that comes
to my mind. Particularly I'm struggling to find a way to use events
between threads.
In particular I'd like to know how to rise and event from a thread and
have it received from the delegate in another thread (I mean the other
thread wakes up and executes the delegate)... And going over... is it
possible to broadcast events? Or, even better, to decide the
destination thread?
I remember that in the past there where messages in windows, not
events, and you could send them through threads safely. But they were
a different thing cos, I think, delegates are just function pointers
and events are bundles of them (ok, with some extras maybe)...
So is it possible to do this with events? Or are there other ways,
similarly easy and convenient (I'm not taling about Monitor, Mutex and
co... I know them...)?
Thanks in advance. Bye.
 
J

james

It looks like what you want is System.Threading.ThreadPool. It keeps
a bunch of threads on standby and wakes them up to process delegates
for you. If you want a dedicated thread look at
System.ComponentModel.BackgroundWorker
 
P

Peter Duniho

[...] Because the UI thread has
a message pump that cycles in a loop, you can inject something to be
processed into that loop, in this case, your delegate call.

For other threads, you pretty much have to do the same thing, that
is,
have a loop waiting for a signal to execute your delegate.

Thanks,
and my question is... How do I do that?

There are two obvious ways, depending on what the nature of the threads
are.

To pass a delegate to a UI thread, all you need to do is call Invoke() or
BeginInvoke() on a Control instance owned by that thread. Typically, this
would be the main form for the application.

To pass a delegate to a non-UI thread, you need to implement your own
version of an event/message queue that can deal with handling delegates.
And of course, the simplest version of that would be to simply have a
queue of delegates (e.g. "Queue<Delegate>"). If the delegate must always
conform to a specific signature, then use the specific delegate type
instead of "Delegate". If not, then you'll probably want to allow the
inclusion of an array of objects to pass as parameters as well (e.g. make
a struct that contains references to both a delegate instance and an
object[], and put that in your queue).

Keep in mind that you of course will want to synchronize access to the
queue, and you will want the processing thread (the one pulling things
from the queue to execute) to wait until some other thread has signaled
it. See the AutoResetEvent class and "lock()" statement for how to do
that.

Pete
 
P

Peter Duniho

It looks like what you want is System.Threading.ThreadPool. It keeps
a bunch of threads on standby and wakes them up to process delegates
for you. If you want a dedicated thread look at
System.ComponentModel.BackgroundWorker

I don't think that's what he wants. It sounds as though he wants a
delegate to be run on a specific thread, not some arbitrary thread pulled
from the thread pool.

I could be wrong, but that's my reading of the thread so far.

Pete
 
T

Tom Spink

Peter said:
I don't think that's what he wants. It sounds as though he wants a
delegate to be run on a specific thread, not some arbitrary thread pulled
from the thread pool.

I could be wrong, but that's my reading of the thread so far.

Pete

Hi Pete,
I could be wrong, but that's my reading of the thread so far.

<big grin>
 
T

Tom Spink

Gotch@ said:
Thanks,
and my question is... How do I do that?


You can not inject a call into the call stack of another thread
without
that thread doing something to be notified of the incoming call. This is
why you can have delegates called on a UI thread. Because the UI thread
has a message pump that cycles in a loop, you can inject something to be
processed into that loop, in this case, your delegate call.

For other threads, you pretty much have to do the same thing, that
is,
have a loop waiting for a signal to execute your delegate.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)


I've recently digged into C# and the whole .Net stuff. Particularly I
found the idea of adding Events and Delegates to the C# language very
interesting and I'm trying to use them in every weird way that comes
to my mind. Particularly I'm struggling to find a way to use events
between threads.
In particular I'd like to know how to rise and event from a thread and
have it received from the delegate in another thread (I mean the other
thread wakes up and executes the delegate)... And going over... is it
possible to broadcast events? Or, even better, to decide the
destination thread?
I remember that in the past there where messages in windows, not
events, and you could send them through threads safely. But they were
a different thing cos, I think, delegates are just function pointers
and events are bundles of them (ok, with some extras maybe)...
So is it possible to do this with events? Or are there other ways,
similarly easy and convenient (I'm not taling about Monitor, Mutex and
co... I know them...)?
Thanks in advance. Bye.

Hi,

I'm glad you're getting into C#. It is pretty superb.

Because what you said sounds really cool, I've whipped up a quick example of
executing a delegate on another thread, and I've made it deliberately
extensible to show that it uses a message-passing design pattern.

It would be rude to post it here, because it's a bit big, so please take a
look at it here:

http://www.betasafe.com/code?view=deleginv
 
P

Peter Duniho

[...]
It would be rude to post it here, because it's a bit big, so please take
a
look at it here:

http://www.betasafe.com/code?view=deleginv

IMHO, it's rude _not_ to post the code here. Well, okay...maybe not so
much "rude" as "short-sighted".

This newsgroup is likely to be archived for some arbitrarily long period
of time. Who knows how long, but the current archives (the largest
repository I know of is now maintained by Google) go back decades. When
you post a link to a web site instead copying the code into the post
itself, you limit the lifetime of the usefulness of your post to that of
your web site. The message itself may live on longer than that (and it
probably will), but it won't be of any use to anyone else at that point.

When will your web site go defunct? One hopes not for a long time, but it
could be tomorrow, next month, next year, whatever. Whatever the
lifetime, it's too short as compared to the likely lifetime of the
newsgroup.

More importantly, if you include the code in your post, the lifetime of
that copy of the code is tied perfectly to the newsgroup, which is exactly
what one wants. Your article's lifetime should match the newsgroup's
lifetime, whether shorter or longer than your web site's, rather than
matching that of your web site.

Besides, while the code might be long, it's certainly no longer than the
huge number of top-posted, untrimmed articles other people post to the
newsgroup every day. I'm not saying two wrongs make a right, but in this
case I don't think it would be wrong to post the code, and in fact is
beneficial, and at the very least would help improve the signal-to-noise
ratio here (admittedly, already better than is found in many other
newsgroups, but still...)

Pete
 
T

Tom Spink

Peter said:
[...]
It would be rude to post it here, because it's a bit big, so please take
a
look at it here:

http://www.betasafe.com/code?view=deleginv

IMHO, it's rude _not_ to post the code here. Well, okay...maybe not so
much "rude" as "short-sighted".

This newsgroup is likely to be archived for some arbitrarily long period
of time. Who knows how long, but the current archives (the largest
repository I know of is now maintained by Google) go back decades. When
you post a link to a web site instead copying the code into the post
itself, you limit the lifetime of the usefulness of your post to that of
your web site. The message itself may live on longer than that (and it
probably will), but it won't be of any use to anyone else at that point.

When will your web site go defunct? One hopes not for a long time, but it
could be tomorrow, next month, next year, whatever. Whatever the
lifetime, it's too short as compared to the likely lifetime of the
newsgroup.

More importantly, if you include the code in your post, the lifetime of
that copy of the code is tied perfectly to the newsgroup, which is exactly
what one wants. Your article's lifetime should match the newsgroup's
lifetime, whether shorter or longer than your web site's, rather than
matching that of your web site.

Besides, while the code might be long, it's certainly no longer than the
huge number of top-posted, untrimmed articles other people post to the
newsgroup every day. I'm not saying two wrongs make a right, but in this
case I don't think it would be wrong to post the code, and in fact is
beneficial, and at the very least would help improve the signal-to-noise
ratio here (admittedly, already better than is found in many other
newsgroups, but still...)

Pete

Hi Pete,

You make some excellent points. I just thought it polite to at least let
people know that a large post would be coming... in the olden days it was
very impolite to post in HTML and/or include images due to peoples limited
connect speeds. I guess with the broadband revolution, I can archive my
code for a long time, like this:

Watch out for line wrapping!

///
using System;
using System.Collections.Generic;
using System.Threading;

public class ProcessorStopMessage : ProcessorMessage
{
public override string ToString ()
{
return "Processor stop message.";
}
}

public delegate void VoidDelegate ();

public class ProcessorInvokeMessage : ProcessorMessage
{
private VoidDelegate _deleg;

public ProcessorInvokeMessage (VoidDelegate deleg)
{
_deleg = deleg;
}

public VoidDelegate Delegate
{
get { return _deleg; }
}

public override string ToString ()
{
return "Delegate invocation message.";
}
}

public class ProcessorMessage
{
public override string ToString ()
{
return "No-op processor message.";
}
}

public class ProcessorContext
{
private int _id;
private ManualResetEvent _messageWaiting;
private Queue<ProcessorMessage> _messageQueue;

public ProcessorContext (int id)
{
_id = id;
_messageWaiting = new ManualResetEvent(false);
_messageQueue = new Queue<ProcessorMessage>();
}

public int ProcessorId
{
get { return _id; }
}

public void PushMessage (ProcessorMessage msg)
{
lock (_messageQueue) {
_messageQueue.Enqueue(msg);
_messageWaiting.Set();
}
}

public ProcessorMessage GetMessage ()
{
_messageWaiting.WaitOne();

lock (_messageQueue) {
ProcessorMessage msg = _messageQueue.Dequeue();

if (_messageQueue.Count == 0)
_messageWaiting.Reset();

return msg;
}
}
}

public class EntryPoint
{
public static void Main ()
{
EntryPoint ep = new EntryPoint();
ep.InternalMain();
}

private void InternalMain ()
{
ProcessorContext[] contexts = new ProcessorContext[5];
Thread[] processors = new Thread[5];

for (int i = 0; i < processors.Length; i++) {
contexts = new ProcessorContext(i);
processors = new Thread(ProcessorThread);
processors.Start(contexts);
}

Console.WriteLine("All {0} processors have been started.",
processors.Length);
Console.WriteLine("Hit enter to invoke the TestDelegate on processor
2");
Console.ReadLine();

contexts[2].PushMessage(new ProcessorInvokeMessage(TestDelegate));

Console.WriteLine("Hit enter to torture the processors.");
Console.ReadLine();

Random rng = new Random();
for (int i = 0; i < 1000; i++) {
contexts[rng.Next(contexts.Length)].PushMessage(new
ProcessorInvokeMessage(TestDelegate));
}

Console.WriteLine("Hit enter to stop the processors.");
Console.ReadLine();
for (int i = 0; i < processors.Length; i++) {
contexts.PushMessage(new ProcessorStopMessage());
}
}

private void ProcessorThread (object param)
{
ProcessorContext context = param as ProcessorContext;

while (true) {
ProcessorMessage msg = context.GetMessage();
Console.WriteLine("Processor {0} received a message:",
context.ProcessorId);
Console.WriteLine(msg.ToString());

if (msg is ProcessorStopMessage)
break;
else if (msg is ProcessorInvokeMessage)
((ProcessorInvokeMessage)msg).Delegate();
}
}

private void TestDelegate ()
{
Console.WriteLine("This delegate is being executed on thread {0}.",
Thread.CurrentThread.ManagedThreadId);
}
}
///
 
N

Nicholas Paldino [.NET/C# MVP]

It should be noted that the naming here implies an assumption about the
Thread class and the framework which is incorrect as of .NET 2.0.

In using "Processor" in the naming of the classes, you imply that the
Thread is tied to a physical thread run on a processor. This is not the
case in .NET 2.0. Threads are logical constructs, which are handled at the
discretion of the host of the runtime.

--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)


Tom Spink said:
Peter said:
[...]
It would be rude to post it here, because it's a bit big, so please take
a
look at it here:

http://www.betasafe.com/code?view=deleginv

IMHO, it's rude _not_ to post the code here. Well, okay...maybe not so
much "rude" as "short-sighted".

This newsgroup is likely to be archived for some arbitrarily long period
of time. Who knows how long, but the current archives (the largest
repository I know of is now maintained by Google) go back decades. When
you post a link to a web site instead copying the code into the post
itself, you limit the lifetime of the usefulness of your post to that of
your web site. The message itself may live on longer than that (and it
probably will), but it won't be of any use to anyone else at that point.

When will your web site go defunct? One hopes not for a long time, but
it
could be tomorrow, next month, next year, whatever. Whatever the
lifetime, it's too short as compared to the likely lifetime of the
newsgroup.

More importantly, if you include the code in your post, the lifetime of
that copy of the code is tied perfectly to the newsgroup, which is
exactly
what one wants. Your article's lifetime should match the newsgroup's
lifetime, whether shorter or longer than your web site's, rather than
matching that of your web site.

Besides, while the code might be long, it's certainly no longer than the
huge number of top-posted, untrimmed articles other people post to the
newsgroup every day. I'm not saying two wrongs make a right, but in this
case I don't think it would be wrong to post the code, and in fact is
beneficial, and at the very least would help improve the signal-to-noise
ratio here (admittedly, already better than is found in many other
newsgroups, but still...)

Pete

Hi Pete,

You make some excellent points. I just thought it polite to at least let
people know that a large post would be coming... in the olden days it was
very impolite to post in HTML and/or include images due to peoples limited
connect speeds. I guess with the broadband revolution, I can archive my
code for a long time, like this:

Watch out for line wrapping!

///
using System;
using System.Collections.Generic;
using System.Threading;

public class ProcessorStopMessage : ProcessorMessage
{
public override string ToString ()
{
return "Processor stop message.";
}
}

public delegate void VoidDelegate ();

public class ProcessorInvokeMessage : ProcessorMessage
{
private VoidDelegate _deleg;

public ProcessorInvokeMessage (VoidDelegate deleg)
{
_deleg = deleg;
}

public VoidDelegate Delegate
{
get { return _deleg; }
}

public override string ToString ()
{
return "Delegate invocation message.";
}
}

public class ProcessorMessage
{
public override string ToString ()
{
return "No-op processor message.";
}
}

public class ProcessorContext
{
private int _id;
private ManualResetEvent _messageWaiting;
private Queue<ProcessorMessage> _messageQueue;

public ProcessorContext (int id)
{
_id = id;
_messageWaiting = new ManualResetEvent(false);
_messageQueue = new Queue<ProcessorMessage>();
}

public int ProcessorId
{
get { return _id; }
}

public void PushMessage (ProcessorMessage msg)
{
lock (_messageQueue) {
_messageQueue.Enqueue(msg);
_messageWaiting.Set();
}
}

public ProcessorMessage GetMessage ()
{
_messageWaiting.WaitOne();

lock (_messageQueue) {
ProcessorMessage msg = _messageQueue.Dequeue();

if (_messageQueue.Count == 0)
_messageWaiting.Reset();

return msg;
}
}
}

public class EntryPoint
{
public static void Main ()
{
EntryPoint ep = new EntryPoint();
ep.InternalMain();
}

private void InternalMain ()
{
ProcessorContext[] contexts = new ProcessorContext[5];
Thread[] processors = new Thread[5];

for (int i = 0; i < processors.Length; i++) {
contexts = new ProcessorContext(i);
processors = new Thread(ProcessorThread);
processors.Start(contexts);
}

Console.WriteLine("All {0} processors have been started.",
processors.Length);
Console.WriteLine("Hit enter to invoke the TestDelegate on
processor
2");
Console.ReadLine();

contexts[2].PushMessage(new ProcessorInvokeMessage(TestDelegate));

Console.WriteLine("Hit enter to torture the processors.");
Console.ReadLine();

Random rng = new Random();
for (int i = 0; i < 1000; i++) {
contexts[rng.Next(contexts.Length)].PushMessage(new
ProcessorInvokeMessage(TestDelegate));
}

Console.WriteLine("Hit enter to stop the processors.");
Console.ReadLine();
for (int i = 0; i < processors.Length; i++) {
contexts.PushMessage(new ProcessorStopMessage());
}
}

private void ProcessorThread (object param)
{
ProcessorContext context = param as ProcessorContext;

while (true) {
ProcessorMessage msg = context.GetMessage();
Console.WriteLine("Processor {0} received a message:",
context.ProcessorId);
Console.WriteLine(msg.ToString());

if (msg is ProcessorStopMessage)
break;
else if (msg is ProcessorInvokeMessage)
((ProcessorInvokeMessage)msg).Delegate();
}
}

private void TestDelegate ()
{
Console.WriteLine("This delegate is being executed on thread {0}.",
Thread.CurrentThread.ManagedThreadId);
}
}
///
 
T

Tom Spink

Nicholas said:
It should be noted that the naming here implies an assumption about
the
Thread class and the framework which is incorrect as of .NET 2.0.

In using "Processor" in the naming of the classes, you imply that the
Thread is tied to a physical thread run on a processor. This is not the
case in .NET 2.0. Threads are logical constructs, which are handled at
the discretion of the host of the runtime.

That's not what I meant to imply in the slightest, the name processor came
from the word processor, which is a noun for "a person or thing that
processes". These threads process messages, which in my view makes them
processors.
 
P

Peter Duniho

That's not what I meant to imply in the slightest, the name processor
came
from the word processor, which is a noun for "a person or thing that
processes". These threads process messages, which in my view makes them
processors.

For what it's worth, that's how I interpreted it (that is, as you
intended). I completely missed any potential relationship between the
thread doing the processing and an actual "central processing unit", or
"processor". :)

That said, I'm not convinced that Nicholas' concern is of great concern
anyway, even if you had intended the connection. My understanding is that
at least for the time being, a Thread in .NET corresponds exactly to an OS
thread. Also, since there's not normally any long-term relationship
between an OS thread and a given logical CPU, the connection between the
naming "Processor" and an actual CPU would be tenuous at best, regardless
of which version of .NET one is using. Even when a .NET thread _does_
correspond to an OS thread, one should not assume that thread is tied to a
given logical CPU (unless of course affinity has been explicitly set).

(And since I'm wandering way off the path here, I'll mention that I hope
that if and when .NET _does_ virtualize threads somehow, dissolving the
current relationship to an OS thread, they still somehow retain the
concept of thread affinity. There are likely to always be situations in
which setting a thread's affinity is important, no matter the logical
model).

Pete
 
N

Nicholas Paldino [.NET/C# MVP]

While it might not be what Tom implied, looking at the code, it's not
hard to make that jump.

It's not a great concern, a name is a name, and they are subjective at
best. There is nothing wrong with the code as it is implemented, nor was
there a comment to that end. The only concern was in the message that was
being delivered if the naming was taken a certain way.

Peter, since .NET 2.0, there has not been a tie between a .NET Thread
object and an underlying OS thread. As I stated in my previous response,
Threads are logical constructs, and they do not always map to an underlying
OS thread. SQL Server, as a CLR host, manages the threads explicitly, and
can map a logical Thread instance to whatever thread/fiber it wants. Any
host can control how threads are managed. It is because of this that the
ManagedThreadId property was introduced, so that a managed id could be
provided which was not tied to the underlying OS thread id (since this is no
longer guaranteed), but could still be used to identify the logical instance
of the thread.

Additionally, when .NET 2.0 was released, the ability to maintain thread
affinity was added, as evidenced by the introduction of the
BeginThreadAffinity and EndThreadAffinity methods on the Thread class.
 

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