Sir,
I apologize if this is al开发者_开发问答ready covered somewhere. I did a search and find something which I have already implemented.
I am developing an application which receives data from a device connected via serialport. I am using SerialPort datareceived event to capture data. I am to display data in text boxes of the main form. Data is received frequently. I used Timer to send command to the device, in response device send some data. Timer interval is 100 ms. In every 100 ms interval some command is sent and correspondingly data is received. I used Invoke function to update the GUI elements like TExtbox, labels etc. Everything is fine. All the elements are updating beautifully. But during receiving data if I make any change in main form like moving form, minimizing, maximizing, or clicking somewhere in the form, then data receiving stops. I couldnot find the reason why its happening ? I also changed the timer interval to 200,300,400,500 but same problem is there.
Please tell me why its happening? And possible solution...
Thanks In advance.... :)
The problem is that Timer is disabled when minimized. You should rather create a new Thread
and in it use Thread.Sleep(100);
to make it sleep. Also, abort it when you are closing. Consider something like:
Thread recieverThread = new Thread(delegate()
{
try
{
//try loading data
Thread.Sleep(100);
}
catch (ThreadAbortException)
{
//close port or something
}
});
//on form.close or something like that
recieverThread.Abort();
This should do the trick. Also if reciever updates interface you MUST use Form.Invoke(...)
to do that because it is running on separate thread.
Why don't you do the operation on a different thread and see.
My guess is that you're crashing your timer thread, because you can't update the Form controls from another thread without issues... you have to create a delegate that runs under the main UI thread. You do this by testing Form.InvokeRequired
and calling Form.Invoke
if it's true.
What's happening: Your timer thread is updating the textboxes or other controls on the form. You resize or minimize, and the handles to those form controls are invalidated... you can't use them anymore. Except your timer thread is still running and trying to use them. Crash!
A good example on multithreaded forms is here. The important part is:
delegate void SetBoolDelegate(bool parameter);
// This would be your timer tick event handler...
void SetInputEnabled(bool enabled) {
if(!InvokeRequired) {
button1.Enabled=enabled;
comboBoxDigits.Enabled=enabled;
numericUpDownDigits.Enabled=enabled;
}
else {
Invoke(new SetBoolDelegate(SetInputEnabled),new object[] {enabled});
}
}
In this example, you test InvokeRequired. If it's false, then you're running on the main UI thread and can set the control properties directly. If it's true, you call Invoke(), passing a function that will be called from the main UI thread.
In this example, the function/delegate you call is the same function you're in, but it doesn't have to be. But you can pass it the timer tick event handler to have that execute on the main thread.
Your UI thread is blocking in a modal loop during the window move operation. As a result any Form.Invoke()
call also blocks.
Your best bet is to use a queue in addition to the background thread that receives the data. The UI thread then just polls for data during a timer event.
Queue access must be synchronized to avoid race conditions. Swap queue instances when the UI thread needs to get the set of updates to apply, since the UI may need to do multiple time-consuming operations.
A queue manager something like this will work (both threads essentially own a queue, and the manager switches them around):
private Queue<T> dataQueue;
private object syncLock = new Object();
private volatile bool dataAvailable; // Volatile to avoid locking on read.
// Called by the background thread to add an event object (e.g. string) to the queue.
void QueuePut(T data)
{
lock (this.syncLock)
{
if (this.dataQueue == null)
{
this.dataQueue = new Queue<T>();
}
this.dataQueue.Add(data);
this.dataAvailable = true;
}
}
// Called by the UI thread to get the pending updates.
Queue<T> QueueGet(Queue<T> oldQueue)
{
if (oldQueue != null)
{
oldQueue.Clear();
}
Queue<T> result;
lock (this.syncLock)
{
result = this.dataQueue;
this.dataQueue = oldQueue;
this.dataAvailable = false;
}
return result;
}
// Called by UI thread to avoid retrieving an empty queue
// (and subsequent reallocation).
public bool IsDataAvailable()
{
get
{
return this.dataAvailable;
}
}
This way the UI doesn't get updated at all when minimized. You may also want to put a limit on the number of items in the background queue if its just for logging or something similar where old events can be purged without harm (otherwise you may end up running out of memory).
精彩评论