5

This is a technique I've used for years to receive network data and use it in my GUI (dialog, form etc).

    public delegate void mydelegate(Byte[] message);

    public ReceiveEngineCS(String LocalIpIn, String ReceiveFromIp, mydelegate d)
    {
         this.m_LocalIpIn = LocalIpIn;
         this.m_ReceiveFromIp = ReceiveFromIp;
         m_MainCallback = d;
         SetupReceive();
         m_Running = true;
         //Create the Track receive thread and pass the parent (this)
         m_RtdirReceiveThread = new Thread(new ParameterizedThreadStart(MessageRecieveThread));
         m_RtdirReceiveThread.Start(this);
    }

    private void MessageRecieveThread(System.Object obj)
    {
         ReceiveEngineCS parent = (ReceiveEngineCS)obj;

         while(parent.m_Running)
         {
              Byte[] receiveBytes = new Byte[1500];
              try
              {
                   receiveBytes = parent.m_ClientReceiver.Receive(ref parent.ipEndPoint);
                   parent.ThreadOutput(receiveBytes);   
              }
              catch ( Exception e )
              {
                  parent.StatusUpdate(e.ToString()); 
              }                         
         }          
    }

    public void ThreadOutput(Byte[] message)
    {
         m_MainCallback(message);           
    }

public partial class SystemMain : Form
{
    //Local Class Variables
    Network.ReceiveEngineCS SystemMessageReceiver;
    private void Form1_Load(object sender, EventArgs e)
    {
        //Load up the message receiver
        SystemMessageReceiver = new Network.ReceiveEngineCS(localAddy, fromAddy, new mydelegate(LocalDelegate));
    }

    public void LocalDelegate(Byte[] message)
    {
        if (Form.ListView.InvokeRequired)
        {
            //External call: invoke delegate
            Form.ListView.Invoke(new mydelegate(this.LocalDelegate), message);
        }
        else
        {
            //Get the Packet Header
            Formats.PacketHeaderObject ph = new Formats.PacketHeaderObject(message);
            //Update or Add item to Specific ListView
            ... update views
        }
    }
 }

The Receiver takes in anywhere between 10 and 100 real-time messages a second and often more.

I've been doing research lately into .Net 4.0 and C# and noticed many other similar ways to do this data processing ,such as Worker Threads, and other ways of using the Delegate and Invoke.

My question... Is there a more efficient way in the newer .Net Libraries (3.5, 4.0 etc) to do this data receive / GUI updating?

I think this method isn't working as well with C#.

Any help would be appreciated.

Sleff
  • 89
  • 1
  • 7
  • 1
    Throwing a hundred notifications per second at a user doesn't make much sense. It will look like a blur. Make your UI usable first, solves your problem too. No changes in Winforms. – Hans Passant Apr 25 '11 at 00:18
  • Nice idea Hans. If I build up meta data about the incoming data in the thread and only update the gui every second or so It would still give a decent user response about the data while allowing the Gui not to get bogged down with updates. Usually the operator uses the rough count of updated data items as a guide to what the data rate is. So counting ten a second 50 a second at a glance etc. But I could just consolidate that as meta updates and add that to the listview. – Sleff Apr 25 '11 at 02:53

2 Answers2

4

One of the best ways of posting updates to a GUI is to have the worker thread package up the data included in the update and place it in a queue. The UI thread will then poll the queue periodically. Having the worker thread use Control.Invoke to update the UI is way overused in my opinion. In contrast, having the UI thread poll for the updates has several advantages.

  • It breaks the tight coupling between the UI and worker threads that Control.Invoke imposes.
  • It puts the responsibility of updating the UI thread on the UI thread where it should belong anyway.
  • The UI thread gets to dictate when and how often the update should take place.
  • There is no risk of the UI message pump being overrun as would be the case with the marshaling techniques initiated by the worker thread.
  • The worker thread does not have to wait for an acknowledgement that the update was performed before proceeding with its next steps (ie. you get more throughput on both the UI and worker threads).

You did not mention how ThreadOutput was implemented, but you might consider the approach I mentioned above if it is not already done that way. Experience has taught me that this is usually the best approach. Letting the UI thread throttle its update cycle is big a advantage.

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
  • The problem I have always had with polling data from a shared resource has been one of timing. Locking and unlocking a shared queue leads to a lot of overhead (not that I'm not having that problem at the moment of course). I have looked into worker threads and saw that there is a mechanism for the thread to send back updates. What I wasn't sure of was whether or not one had to use an invoke for the worker thread to update a UI owned queue? – Sleff Apr 25 '11 at 03:05
  • The locking of a queue is nothing compared to `Control.Invoke` which is an extremely expensive operation. Plus, you could always use `ConcurrentQueue` which uses lock-free mechanisms. The worker thread does not have to use `Invoke` to post something to a queue. It might be a bit misleading to call it a "UI owned queue" in the first place. – Brian Gideon Apr 25 '11 at 12:54
  • That helps @Brian. I was unsure of the expense of invoke. I had always seen it used in simple examples of updating the gui from simple files or low-rate xml data. I'll check out ConcurrentQueue and also look into cutting down what needs to update as @Hans suggested. – Sleff Apr 25 '11 at 16:27
  • I would like to know a little more about how this is implemented. Do you create a shared queue(array/dictionary) on the UI thread that worker threads insert their messages into? Will the queue have to hold a reference to each ui control it wants to update? Are there any links you can point us to? – goku_da_master Sep 10 '12 at 19:16
1

Take a look into Thread Pooling instead of spinning new thread each time.

Zamboni
  • 7,897
  • 5
  • 43
  • 52
  • Thanks Zamboni but I'm actually only using one thread for message receive the entire lifetime of the app. The messages are continous updates of events that I need to receive at all times. – Sleff Apr 25 '11 at 02:46