App sporadically hangs while raising event while processing system.threading.timer event

D

Dan

I've created a pocketpc app which has a startup form containing a listview.
The form creates an object which in turn creates a System.Threading.Timer.
It keeps track of the Timer state using a TimerState object similar to the
example in the System.Threading.Timer documentation. The method which
handles the timer events, among other things, periodically calls a method in
this TimerState object which raises an event to the startup form, which in
turn adds a record to the listview. Sporadically this code in the startup
form hangs when it tries to add the record to the listview:



listView1.Items.Add(lvi).



I first noticed this happening when I happend to dock the iPAQ while it was
adding an item. The ActiveSync messagebox came up and hung, as did my
process. I am now able to reproduce it at will by adding a MessageBox call
prior to raising the event. The messagebox call returns but the messagebox
itself remains displayed on the screen, and my process hangs at the line
mentioned above.



Anyone have any thoughts on what's going on? The details of the
implementation are described below....Dan





1. Form1 creates an UploadLooper object.

2. UploadLooper creates a TimerState object, similar to the example in
the System.Threading.Timer documentation.

3. The UploadLooper module also contains a delegate declaration, along
with a declaration for an UploadEvent object derived from EventArgs:



public delegate void StatusEvent(Object sender, UploadEvent e);



4. The TimerState object declares the event and contains a RaiseStatus
method which enables us to fire StatusEvent:



public class TimerState

{

public event StatusEvent StatusMessage; // Declaration of the event

....



public void RaiseStatus(string message)

{

Tracer.WriteLine("TimerState.RaiseStatus", "Before raising event");

if (StatusMessage != null)

StatusMessage(this, new UploadEvent(message));

Tracer.WriteLine("TimerState.RaiseStatus", "After raising event");

}



5. The TimerState object also contains a Timer property itself, which
references the Timer we are going to create. We start the timer via the
UploadLooper.StartTimer method as follows:



public void StartTimer(uint millisecondInterval)

{

// Create the delegate that handles the timer events

System.Threading.TimerCallback timerDelegate = new
TimerCallback(CheckStatus);

// Create a timer that waits one second, then invokes every
'millisecondInterval' milliseconds

// and give TimerState a reference to the timer so we can dispose it when
necessary

m_TimerState.TimerInterval = millisecondInterval;

m_TimerState.Timer = new System.Threading.Timer(timerDelegate, m_TimerState,
(uint) 5000, millisecondInterval);

}



6. The CheckStatus method gets called when the Timer fires, and casts
the object it receives to a TimerState object:



static void CheckStatus(Object state)

{

TimerState timerState = (TimerState) state;

timerState.Timer.Change(Timeout.Infinite, Timeout.Infinite); // Disable the
timer





7. CheckStatus then goes on to do its processing. Periodically, calls
the TimerState.RaiseStatus method, which raises an event. Form1 catches the
event and adds a ListViewItem to a ListView:



private void AddNewStatus(string message)

{

Tracer.WriteLine("Form1.AddNewStatus", "Entering sub. Message: " + message);

ListViewItem lvi = new ListViewItem(DateTime.Now.ToString("hh:mm:ss"));

Tracer.WriteLine("Form1.AddNewStatus", "After creating lvi");

lvi.SubItems.Add(message);

Tracer.WriteLine("Form1.AddNewStatus", "After adding subitem");

listView1.Items.Add(lvi);

Tracer.WriteLine("Form1.AddNewStatus", "After adding lvi to listView");

listView1.Refresh();

Tracer.WriteLine("Form1.AddNewStatus", "Exiting sub.");

}



8. Sporadically, the AddNewStatus method hangs on
listView1.Items.Add(lvi). I first noticed this happening when I happend to
dock the iPAQ while it was adding an item. The ActiveSync messagebox came
up and hung, as did my process. I am now able to reproduce it at will by
adding a MessageBox call prior to raising an event. The messagebox call
returns but the messagebox itself remains displayed on the screen, and my
process hangs at the line mentioned above.
 
G

Guest

Well... and now for the hundred's time... You're trying to update the UI
thread from a different (Timer's) thread. You MUST use Control.Invoke in
order to do that.
 
D

Dan

If that is true, then why does this code *ever* work? (It almost always
does).

Also-- can you point me to a good article on Invoke? Thanks very much...Dan
 
D

Dan

Daniel, another respondent to this thread, pointed me to the following
article, which points out a way to work around the compact framework
limitation that you are unable pass arguments via Invoke:
http://www.zen13120.zen.co.uk/Blog/2004/10/invoke-cf-and-full-fx.html

I am thinking of implementing the workaround as follows:

1. Create a Queue object in the object from which I start the timer thread
2. When I want to pass an argument from the worker thread, SyncLock the
Queue, Enqueue the argument ( a string) to the queue, then End SyncLock the
queue.
3. Invoke an EventHandler in the GUI thread
4. The EventHandler gets a reference to the Queue object from the object
encapsulating the worker thread.
5. In the EventHandler, SyncLock the retrieved Queue object, Dequeue any
enqueued objects, and End Synclock the retrieved Queue.
6. Add the retrieved string to the listview

Does anyone see any problems with this?

Thanks...

Dan
 

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