Reputation: 3111
i need some clarifications about the multithreading programming model, becuase, rarely i have to deal with it (mostly all the time i do web-programming).
i have a program that i have made in C# basically it's a data monitor and execute pooling every specific ammount of time over the database, let's say 1 minute.
How this application is relatively simple i decided use a monolithic structure, i made the all the GUI in range of classes, and i made an object that deals with the connection and data retrieving.
to deal with the requeriment of the pooling time i used a System.Timers.Timer
, which is basically a Thread wrapping with a firing timed event, here i executed the data pooling.
When new data is found i need to fire a method in X class that belongs to the GUI (now running in the main thread) from the function triggered by the timer (in other thread by definition).
the first time i tried to call the method directly to the method triggered by the timer, resulting in a cross-thread error: because the method resides in X class and was not contructed in the timer thread.
Then i tried again using other approach. I created a event with his respective delegate in the class that read the data and subscribe the GUI objects to that event.
now when i fire the event .net produces an exception saying me that the sender object instance is null, exactly don't know why, but i suspects that is because the object itself it's created with the GUI objects in the main thread and when i decide to send the object from the other thread (that it's generating the event), the instance simply doesn't exists because wasn't created there.
This is the function called when the timer expires:
private void PoolearMensajes(object Sender, ElapsedEventArgs e)
{
try
{
pRdr.getPendingMessages(); <--- This function creates the exception
if (pRdr.MensajesPendientes > 0)
{
this.oSysIcon.Text = pRdr.MensajesPendientes.ToString() + " tickets pendientes";
this.oSysIcon.BalloonTipIcon = ToolTipIcon.Info;
this.oSysIcon.BalloonTipText = pRdr.MensajesPendientes.ToString() + " tickets pendientes";
this.oSysIcon.BalloonTipTitle = "Información";
this.oSysIcon.ShowBalloonTip(3000);
System.Media.SystemSounds.Exclamation.Play();
}
}
catch (Exception ex)
{
this.oSysIcon.BalloonTipTitle = "Error";
this.oSysIcon.Text = "Ha ocurrido un error.";
this.oSysIcon.BalloonTipIcon = ToolTipIcon.Error;
this.oSysIcon.BalloonTipText = ex.Message;
this.oSysIcon.ShowBalloonTip(3000);
System.Media.SystemSounds.Exclamation.Play();
}
}
Here is the method resides in the data retriever object that was created in main thread:
public delegate void TicketsFoundHandler(object sender, TicketEventArgs ta);
public event TicketsFoundHandler TicketsFound;
public void getPendingMessages()
{
string sSQL;
sSQL = " an SQL Query that counts rows;";
oCmd = new SqlCommand(sSQL, oConn);
oConn.Open();
this.i_pendmsg = (int)oCmd.ExecuteScalar();
if (this.i_pendmsg > 0)
{
TicketEventArgs ta = new TicketEventArgs("", this.i_pendmsg);
TicketsFound(this, ta); <---- ERROR!
}
oConn.Close();
}
The Error message it's: Object reference not set to an instance of an object
.
any ideas , any improvements, or something i haven't cosidered ?
Upvotes: 0
Views: 421
Reputation:
the first time i tried to call the method directly to the method triggered by the timer, resulting in a cross-thread error: because the method resides in X class and was not contructed in the timer thread.
You were almost there. You just need to call BeginInvoke()
to invoke your operation on the GUI thread.
You might also find that using a BackgroundWorker
is even easier.
EDIT:
Your specific error in your question is because nothing has subscribed to your TicketsFound
event. Use TicketsFound += ...
to do so. Also, check for null before raising the event:
if (TicketsFound != null) TicketsFound(...)
Upvotes: 2