Events in threads...

D

Danny Tuppeny

Hi All,

I've written a console app, which sends and recieves data across a
NetworkStream and displays output to the console. All is great.

I'm now modifying it to be a windows app, but the thing blocks while waiting
to recieve data. So, I want to run it in a thread. However, the object
that's running my events fires events, and these all need to update the UI.
What's the best way of doing this? Should I call BeginInvoke from inside my
event handlers, or should my object raise these events on the main thread
anyway?

Most of my threading experience is using the BackgroundWorker class, but
this doesn't really fit my needs (using ProgressUpdate owuld be a hack ;o))

Below is my main class code. pokerServer.Go() needs to be run inside a
thread, but the event needs to write to my listbox. Any recommendations on
the best way?

Thanks,

(note, this is .net2)

namespace DTuppeny.PokerBot
{
public partial class TuppoBot : Form
{
PokiPokerServer pokerServer;

public TuppoBot()
{
InitializeComponent();

pokerServer = new PokiPokerServer();
pokerServer.Log += new LogHandler(pokerServer_Log);
}

void pokerServer_Log(string s)
{
listLog.Items.Add(s);
}

private void btnJoin_Click(object sender, EventArgs e)
{
pokerServer.Go();
}
}
}
 
D

Danny Tuppeny

<snip>

Now I'm wondering if my .Go() call should be as is, and inside that, I
should fire up a thread. So from the outside, everything is the UI thread,
but the threading is taken car of inside the class. Would this be more
sensible?
 
R

Richard Blewett [DevelopMentor]

Danny Tuppeny said:
<snip>

Now I'm wondering if my .Go() call should be as is, and inside that, I
should fire up a thread. So from the outside, everything is the UI thread,
but the threading is taken car of inside the class. Would this be more
sensible?

It depends :)

If you want the class to be reusable in Console and Winforms apps then you
shouldn't automatically handle the threading inside the class (what will you
use to marshal the call to the UI thread in a console). If I were you I'd
have a two constructors:

1) one that takes an instance of ISynchronizedInvoke
2) one that doesn't

If you get passed an ISynchronizedInvoke reference then take care of the
threading inside the class, otherwise call the events on the background
thread.

This is the approach taken by System.Timers.Timer

(ISynchronizedInvoke is the interface that Control implements to get
BeginInvoke, InvokeRequired, etc)

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk
 
D

Danny Tuppeny

"Richard Blewett [DevelopMentor]" <richard at nospam dotnetconsult dot co
dot uk> wrote in message news:%[email protected]...
It depends :)

If you want the class to be reusable in Console and Winforms apps then you
shouldn't automatically handle the threading inside the class (what will
you use to marshal the call to the UI thread in a console). If I were you
I'd have a two constructors:

1) one that takes an instance of ISynchronizedInvoke
2) one that doesn't

If you get passed an ISynchronizedInvoke reference then take care of the
threading inside the class, otherwise call the events on the background
thread.

This is the approach taken by System.Timers.Timer

(ISynchronizedInvoke is the interface that Control implements to get
BeginInvoke, InvokeRequired, etc)

That sounds like a plan. So if I have ISynchronizedInvoke, I start a new
thread inside my class. When I need to fire an event, can I pass the event
delegate straight to BeginInvoke or should I wrap each one up? eg., could I
change:

public delegate void LogHandler(string s);
public event LogHandler Log;

void WriteLog(string s)
{
if (Log != null) // Log is
{
Log(s);
}
}

to something like this? How would I pass the argument? (I'll play around
when I get back, but if anyone has the answer in the meantime..!)

public delegate void LogHandler(string s);
public event LogHandler Log;

void WriteLog(string s)
{
if (Log != null) // Log is
{
// whta do I do with s?
syncro.BeginInvoke(Log)
}
}
 
N

Nicholas Paldino [.NET/C# MVP]

Danny,

Juval Lowy has written an EventsHelper class which will do all of this
for you. You can find it at:

http://www.idesign.net/idesign/BAC/uploads/EventsHelper.zip

It will take an event and fire it for you (correctly handling certain
threading issues and whatnot as well).

The difference between his implementation and yours is that it will
check the target of every event handler. If the target implements
ISynchronizeInvoke, it will pass the delegate to that to be fired.

Hope this helps.


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

Danny Tuppeny said:
"Richard Blewett [DevelopMentor]" <richard at nospam dotnetconsult dot co
dot uk> wrote in message news:%[email protected]...
It depends :)

If you want the class to be reusable in Console and Winforms apps then
you shouldn't automatically handle the threading inside the class (what
will you use to marshal the call to the UI thread in a console). If I
were you I'd have a two constructors:

1) one that takes an instance of ISynchronizedInvoke
2) one that doesn't

If you get passed an ISynchronizedInvoke reference then take care of the
threading inside the class, otherwise call the events on the background
thread.

This is the approach taken by System.Timers.Timer

(ISynchronizedInvoke is the interface that Control implements to get
BeginInvoke, InvokeRequired, etc)

That sounds like a plan. So if I have ISynchronizedInvoke, I start a new
thread inside my class. When I need to fire an event, can I pass the event
delegate straight to BeginInvoke or should I wrap each one up? eg., could
I change:

public delegate void LogHandler(string s);
public event LogHandler Log;

void WriteLog(string s)
{
if (Log != null) // Log is
{
Log(s);
}
}

to something like this? How would I pass the argument? (I'll play around
when I get back, but if anyone has the answer in the meantime..!)

public delegate void LogHandler(string s);
public event LogHandler Log;

void WriteLog(string s)
{
if (Log != null) // Log is
{
// whta do I do with s?
syncro.BeginInvoke(Log)
}
}
 
D

Danny Tuppeny

Nicholas Paldino said:
Danny,

Juval Lowy has written an EventsHelper class which will do all of this
for you. You can find it at:

http://www.idesign.net/idesign/BAC/uploads/EventsHelper.zip

It will take an event and fire it for you (correctly handling certain
threading issues and whatnot as well).

The difference between his implementation and yours is that it will
check the target of every event handler. If the target implements
ISynchronizeInvoke, it will pass the delegate to that to be fired.

Thanks, sounds good. The link is a 404 though :(

There's one by Roy Osherov all over Google - I'll have a look if that's the
same :)
 
D

Danny Tuppeny

<snip>

Thanks Richard & Nicholas, it's running fine now, with very little
modifcation from the Console App!

I started the game off with a BackgroundWorker, like this:

private void btnJoin_Click(object sender, EventArgs e)
{
bwGame.RunWorkerAsync();
}

private void bwGame_DoWork(object sender, DoWorkEventArgs e)
{
pokerServer.Go();
}

And fire my events with the EventsHelper, like this:


class PokiPokerServer
{
public event EventHandler<LogEventArgs> Log;

void WriteLog(string s)
{
EventsHelper.FireAsync(Log, this, new LogEventArgs(s));
}
}

and subscribe like this:

pokerServer.Log += new EventHandler<LogEventArgs>(pokerServer_Log);


Job's a good'en! :)
 

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

Similar Threads


Top