Click here to Skip to main content
15,889,116 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
I am building an application (using WPF) that involves sending data via serial port. I have a small (or big) problem when there is a lot of data to send or receive.
The data is send/received in lines with a small delay between each line. For implementing the delay I used the following code - (the AllowUIToUpdate routine I found it ready made and am not sure if there is a better way). I use this code instead of a normal Sleep to update a textblock while sending/receiving the data.

C#
private void waitms(int ms)
{
    bool done = false;
    int val1 = Environment.TickCount;
    while (!done)
    {
        if ((Environment.TickCount - val1) > ms) done = true;
        AllowUIToUpdate();
    }
}
void AllowUIToUpdate()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate(object parameter)
    {
        frame.Continue = false;
        return null;
    }), null);
    Dispatcher.PushFrame(frame);
}


Anyway this works fine for a few lines of data but when I have many lines of data to send / receive, the system starts to slow down really bad. I have coded a 500ms delay between each line. The first lines follow that pretty well but as the lines increase, the delay starts getting bigger and bigger and at the end I have up to 5seconds delay between each line.

So what is causing this slowdown? Can it be the routine above or the serial communication itself? Any ideas?


C#
private void fprogram_Click(object sender, RoutedEventArgs e)
{
    DateTime now = DateTime.Now;
    int dataLength = 0;
    byte[] data;
    labelComms.Content = "Programming";
    if (serial.IsOpen)
    {
        for (int j = 0; j < _modnames.Count; j++)
        {
            for (int k = 0; k < 2; k++)
            {
                textBlock1.Inlines.Add("\n\n Programming Module " + _modnames[j] + k.ToString("D1") + "\n");
                waitms(1000);
                for (int i = 0; i < 21; i++)
                {
                    System.Threading.Thread.Sleep(100);
                    now = DateTime.Now;
                    textBlock1.Inlines.Add(now.ToString("\nhh:mm:ss - " ));
                    for (int m = 0; m < 5; m++)
                    {
                        textBlock1.Inlines.Add(_txbuffer[m].ToString("X2"));
                    }
                    scrollview1.ScrollToEnd();
                    for (int m = 0; m < 6; m++)
                    {
                        _txbuffer[(5+m)] = eprom[(m+(k*6)), i, j];                                  // fill TXbuffer
                        textBlock1.Inlines.Add(_txbuffer[(5+m)].ToString("X2"));          // write to Textblock
                    }
                    System.Threading.Thread.Sleep(100);                                             //wait 100ms
                    serial.DiscardInBuffer();                                                      // flush read buffer
                    serial.Write(_txbuffer, 0, 11);                                                // send TX buffer
                    //receive
                    bool timeOut = false;
                    int val1 = Environment.TickCount;
                    dataLength = 0;
                    while ((dataLength < 11) && (timeOut == false))     //wait untill RXbuffer has 11characters
                    {
                        dataLength = serial.BytesToRead;
                        if ((Environment.TickCount - val1) > 1000) timeOut = true;
                        AllowUIToUpdate();                         // Update UI - otherwise textblock will not be updated
                    }
                    System.Threading.Thread.Sleep(10);
                    if (timeOut == false)
                    {
                        dataLength = serial.BytesToRead;
                        data = new byte[dataLength];
                        int nbrDataRead = serial.Read(data, 0, dataLength);                         //read Data
                        textBlock1.Inlines.Add("  -  ");
                        foreach (byte chr in data)
                        {
                            textBlock1.Inlines.Add(chr.ToString("X2"));       // write to textblock
                        }
                        textBlock1.Inlines.Add(" .... ");
                        if ((data[0] == Comms.mod_initrx1) && (data[1] == Comms.mod_initrx2) && (data[2] == _txbuffer[2]) && (data[3] == _txbuffer[3]) && (data[5] == 0x88))
                        {
                            textBlock1.Inlines.Add("  -  OK");                      // receive data OK
                        }
                        else textBlock1.Inlines.Add("  -  NOT OK");                // receive data NOT OK
                    }
                    else
                    {
                        textBlock1.Inlines.Add("  === TIMEOUT \n");           //Timeout on waiting for receive
                        break;
                    }
                }
            }
        }
        textBlock1.Inlines.Add("\n\n  -  DONE   -\n\n");
    }
    else textBlock1.Inlines.Add("\n   NOT CONNECTED!!  \n ");
}


Ok here is the function I am using for sending /receiving the data. I tried to change waitms to Sleep where possible but if I change them all the the textblock will not be updated.
So can you point out how to put the serial comm in a separate thread as I am still a very beginner on WPF. Thanks.
Posted
Updated 10-Apr-11 23:57pm
v2
Comments
Perry Bruins 10-Apr-11 13:07pm    
so... Why are you not sending the data on a separate thread. Then you do not need to worry about calling the AllowUIToUpdate() routine Implementing a wait routine the way you did it does not feel right to me; if you are on a separate thread you can just call Sleep() which will do the job for you.
Sergey Alexandrovich Kryukov 10-Apr-11 16:19pm    
Perry, this is the important note (I would up-vote it if I could :-)
Not just "Sleep" though -- please see my Answer for pretty much comprehensive advice.
--SA
Perry Bruins 10-Apr-11 16:32pm    
Thanks man, but you did the hard work by giving all the examples and everything; the credit goes to you...

From this code, it is not clear what delay in serial communication is required and why, but the implementation of the delay is wrong anyway. You use spin wait which should be avoided by any means. Spin wait use 100% of the CPU core without any purposes.

All wait methods should be based on thread methods. 1) To wait unconditionally for certain amount of time, use System.Threading.Thread.Sleep. 2) If you need to wait until certain condition, always use System.Threading.EventWaitHandle.WaitOne (with timeout or not). In both cases, the thread is switched off by OS and never scheduled back to execution until waken up by OS. It will be waken up by expiration of time, or the following methods called from another thread: Thread.Abort or System.Threading.EventWaitHandle.Set (second one to be used if a thread is in the wait state at the call to System.Threading.EventWaitHandle.WaitOne on the same instance of EventWaitHandle.

Do I even have to mention that your serial communication should be done in a separate thread, not UI thread? This is absolutely important.

Now, it looks like by some reason you need to wait until the UI is fully updated with new data. First, think again, do you really need it? If you insist on that (yes, it can be a good solution by several reasongs), it can be achieved a bit simpler. You can use Dispatcher.Invoke instead of Dispatcher.BeginInvoke. First method delays the thread calling Invoke be the end of the actual call of the delegate, while BeginInvoke will return as soon as delegate instance and the data used for the call are placed to the invocation queue of the UI thread.

For more detail on invocation, see also my past Answers:
Control.Invoke() vs. Control.BeginInvoke()[^]
Problem with Treeview Scanner And MD5[^]

[EDIT]
You need certainly to put port communications in a separate thread, good point.
The main design idea is: as you do the communications during all the life time of your application, it can be created from the very beginning and kept running. It can be synchronized using thread synchronization primitives.
Best way to create such a thread is to use my thread wrapper:
How to pass ref parameter to the thread[^].
Other options include using a thread pool (you can create a similar wrapper) or BackgroundWorker (which I would rather recommend for temporary tasks.
A good way to organize synchronized communication is using a generic blocking queue. See my Tips/Tricks article and the "alternative" answer (not really alternative, it's just the advice to use the similar class available in v.4.0):
Simple Blocking Queue for Thread Communication and Inter-thread Invocation[^].

At to WPF, it is important to know that you cannot call any UI elements from non-UI thread, so you need to use inter-thread invocation. For WPF, you should use Dispatcher.Invoke or Dispatcher.BeginInvoke. See my other Answers for the explanation of how it works and the samples:
Control.Invoke() vs. Control.BeginInvoke()[^]
Problem with Treeview Scanner And MD5[^].

—SA
 
Share this answer
 
v4
Comments
Espen Harlinn 10-Apr-11 15:58pm    
Good advice, my 5
Sergey Alexandrovich Kryukov 10-Apr-11 16:19pm    
Thank you, Espen.
--Sa
Moonwalker031 11-Apr-11 16:58pm    
Can you please see the edited question and answer back. Thanks.
Sergey Alexandrovich Kryukov 11-Apr-11 17:11pm    
I've added some details based on your update. Please see my updated Answer.
--SA
Sergey Alexandrovich Kryukov 11-Apr-11 17:14pm    
Your big mistake is the whole Click event handler. You do thing is the UI threads which you should only do in a separate thread. You should never use Thread.Sleep, go to any kind of wait condition, do any blocking calls in this thread -- all that belongs in a separate thread. You should never use a spin-wait (some wait in cycle), no matter in what thread.
--SA
Take a look at revision 1 of Barcode scanning[^] - as there is a lot more code provided with the first revision.

As SAKryukov points out, it's probably a good idea to leverage multi-threading. .Net provides ThreadPool.QueueUserWorkItem[^] as a simple mechanism for implementing functionality using worker threads.

Regards
Espen Harlinn
 
Share this answer
 
Comments
Sergey Alexandrovich Kryukov 10-Apr-11 16:21pm    
Agree, my 5. Thread pool is one of the best options, but others can also be used, depending on the project's features.
--SA
Espen Harlinn 10-Apr-11 16:31pm    
Thanks SAKryukov!
After testing further I managed to locate the performance problem I mentioned in the original question.
It has all to do with the TextBlock. I replaced it with a TextBox and now my application works just fine even without Threads. Ofcourse I will be studying Threads and try to implement them in my application but in the meantime it is working as intended.

So for others who might experience the same timing problem, just use a TextBox and AppendText() instead of TextBlock.Inlines.Add(). The latter works fine for few lines but becomes very very slow when you have many lines. That was causing all my timing problems.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900