Can a separate thread own a form control?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • stumorgan@gmail.com

    Can a separate thread own a form control?

    Basically what I have is a form with a graph on it which graphs data
    that I'm reading from a USB device at 100 Hz (every 10ms). I have a
    thread reading and parsing the data from the USB, but when it comes
    time to draw that data on the graph the work is handled on the main UI
    thread through a callback delegate since the form owns the graph
    control. Is there a way I can have a separate thread own the graph
    control and handle the drawing so that the rest of the UI is not
    affected?

    Thanks in advance,
    Stu
  • Peter Duniho

    #2
    Re: Can a separate thread own a form control?

    On Tue, 18 Nov 2008 09:09:55 -0800, <stumorgan@gmai l.comwrote:
    Basically what I have is a form with a graph on it which graphs data
    that I'm reading from a USB device at 100 Hz (every 10ms). I have a
    thread reading and parsing the data from the USB, but when it comes
    time to draw that data on the graph the work is handled on the main UI
    thread through a callback delegate since the form owns the graph
    control. Is there a way I can have a separate thread own the graph
    control and handle the drawing so that the rest of the UI is not
    affected?
    What do you mean by "not affected"? In what way is "the rest of the UI"
    being affected by your graphing that you believe would be addressed by
    moving the graphing to a different thread?

    The short answer is: yes, you can create your graphing control on a
    different thread, and as long as you provide a message pump on that
    thread, it should work fine.

    But I fail to see the advantage in doing so. It would be helpful if you
    could be more specific about what problem it is you perceive and which you
    want to solve.

    Pete

    Comment

    • Ben Voigt [C++ MVP]

      #3
      Re: Can a separate thread own a form control?

      This is obviously a trimmed down version of the whole thing, but
      should show you what I'm doing. ProcessUsbData is the thread which
      reads in the data and calls the functions to update the graph and
      textboxes. If I take out the UpdateO2Textbox (O2) and UpdateCO2Textbo x
      (CO2) calls then the symptoms I'm seeing crop up. By the way, please
      feel free to let me know if any of my code sucks and could be done in
      a more efficient manner. For instance, if I'm supposed to be reading
      from a USB once every 10 seconds is there a better way to do it than
      doing Thread.Sleep(10 ) and then checking the inbound buffer? Thanks
      again!
      You don't need Thread.Sleep, because ReadLine should efficiently wait until
      data is available.

      bAbortRead should be volatile, because it changes from a different thread
      than is reading it, with no synchronization .

      You're making four cross-thread calls every time which is hurting
      performance. And you should use MethodInvoker for maximum performance. Try
      this:

      volatile bool bAbortRead; // bool fields are initialized to false
      by the compiler
      private void ProcessUsbData( )
      {
      string[] data;
      DateTime startTime = DateTime.Now;

      while(!bAbortRe ad)
      {
      data = device.ReadLine ().Split(new char[]{','});

      decimal ts = (DateTime.Now - startTime).Tota lSeconds;

      decimal O2 = 0;
      if(data.Length O2INDEX)
      decimal.TryPars e(data[O2INDEX], out O2);

      decimal CO2 = 0;
      if(data.Length CO2INDEX)
      decimal.TryPars e(data[CO2INDEX], out CO2);

      MethodInvoker updateGUI = delegate {
      if(O2 0 && O2 < 100)
      {
      AddDataPoint("O 2", ts, O2);
      UpdateO2Textbox (O2);
      }
      if(CO2 0 && CO2 < 100)
      {
      AddDataPoint("C O2", ts, CO2);
      UpdateCO2Textbo x(CO2);
      }
      };

      if (InvokeRequired )
      Invoke(updateGU I);
      else
      updateGUI();
      }
      }

      >
      public partial class frmSampleForm : Form
      {
      private bool bAbortRead;
      >
      Thread BufferReader;
      delegate void AddDataPointCal lback(string series, decimal x,
      decimal y);
      delegate void UpdateO2Textbox Callback(decima l pValue);
      delegate void UpdateCO2Textbo xCallback(decim al pValue);
      >
      public frmTesting_Veri fySetup(ref cExercise_Test pTest)
      {
      InitializeCompo nent();
      >
      ReadUSB();
      }
      >
      private void ReadUSB()
      {
      // Start the thread which reads the inbound buffer
      bAbortRead = false;
      BufferReader = new Thread(new ThreadStart(Pro cessUsbData));
      BufferReader.St art();
      }
      >
      private void ProcessUsbData( )
      {
      string[] data;
      decimal O2;
      decimal CO2;
      TimeSpan ts;
      DateTime startTime = DateTime.Now;
      >
      while(!bAbortRe ad)
      {
      data = device.ReadLine ().Split(new char[]{','});
      >
      if(data.Length O2INDEX)
      decimal.TryPars e(data[O2INDEX], out O2);
      else
      O2 = 0;
      >
      if(data.Length CO2INDEX)
      decimal.TryPars e(data[CO2INDEX], out CO2);
      else
      CO2 = 0;
      >
      ts = DateTime.Now - startTime;
      if(O2 0 && O2 < 100)
      {
      AddDataPoint("O 2", (decimal)(ts.To talSeconds),
      O2);
      UpdateO2Textbox (O2);
      }
      if(CO2 0 && CO2 < 100)
      {
      AddDataPoint("C O2", (decimal)(ts.To talSeconds),
      CO2);
      UpdateCO2Textbo x(CO2);
      }
      >
      Thread.Sleep(10 );
      }
      }
      >
      private void AddDataPoint(st ring series, decimal x, decimal y)
      {
      // InvokeRequired required compares the thread ID of the
      // calling thread to the thread ID of the creating thread.
      // If these threads are different, it returns true.
      if(chart1.Invok eRequired)
      {
      AddDataPointCal lback d = new AddDataPointCal lback
      (AddDataPoint);
      Invoke(d, new object[] { series, x, y });
      }
      else
      {
      chart1.Series[series].Points.AddXY(x , y);
      chart1.Invalida te();
      }
      }
      >
      private void UpdateO2Textbox (decimal pValue)
      {
      // InvokeRequired required compares the thread ID of the
      // calling thread to the thread ID of the creating thread.
      // If these threads are different, it returns true.
      if(txtO2.Invoke Required)
      {
      UpdateO2Textbox Callback d = new
      UpdateO2Textbox Callback (UpdateO2Textbo x);
      Invoke(d, new object[] { pValue });
      }
      else
      {
      txtO2.Text = pValue.ToString ("0.00");
      }
      }
      >
      private void UpdateCO2Textbo x(decimal pValue)
      {
      // InvokeRequired required compares the thread ID of the
      // calling thread to the thread ID of the creating thread.
      // If these threads are different, it returns true.
      if(txtCO2.Invok eRequired)
      {
      UpdateCO2Textbo xCallback d = new
      UpdateCO2Textbo xCallback(Updat eCO2Textbox);
      Invoke(d, new object[] { pValue });
      }
      else
      {
      txtCO2.Text = pValue.ToString ("0.00");
      }
      }
      >
      }

      Comment

      • Registered User

        #4
        Re: Can a separate thread own a form control?

        On Tue, 18 Nov 2008 09:09:55 -0800 (PST), stumorgan@gmail .com wrote:
        >Basically what I have is a form with a graph on it which graphs data
        >that I'm reading from a USB device at 100 Hz (every 10ms). I have a
        >thread reading and parsing the data from the USB, but when it comes
        >time to draw that data on the graph the work is handled on the main UI
        >thread through a callback delegate since the form owns the graph
        >control. Is there a way I can have a separate thread own the graph
        >control and handle the drawing so that the rest of the UI is not
        >affected?
        >
        Having followed the thread these thoughts come to mind. The issue
        seems to be the side effects of trying to maintain the data display in
        real-time. I recently ran into a similar issue writing a CAT
        controller/monitor for a device with minimal documentation.


        The device has thirteen buttons, seven rotary controls (three
        concentric plus one), six LEDs and a ~2x3 inch LCD screen. All the
        controls are multifunction. One of the things the LCD display can do
        is act as a spectrum scope. This gives an idea of how much and the
        kind of information the UI might be expected to display.

        Each request/response pair exchanged describes part of the current or
        desired state of the device. The state information is sent to the
        device using five byte requests. Depending upon the request the
        response may be zero to five bytes with a RTT of up to ~5 ms. Multiple
        requests are needed to fully describe the device's state. At any time
        the polling may be interrupted to send a request to the device to
        change state.

        Trying to update the numerous UI controls in real-time resulted in a
        sluggish UI. My solution was to create an type which represents the
        device's state to the UI. It is a composite object that essentially is
        a middle tier between the device and UI. A worker thread (the
        controller thread) runs a loop that manages various custom thread
        objects. The callbacks go to the worker thread not the UI thread. Each
        polled response partially updates the state.

        This scheme lets the BL/TL/device wrappers to be packaged UI-free. A
        timer in the UI fires periodically to update numerous UI controls with
        data read from properties the clean, simple interface the state object
        presents to the UI.. Changes to the device state are made from the UI
        by changing state object's properties.

        regards
        A.G.









        Comment

        Working...