2

I have a small program that's supposed to sample some value from a device connected via USB. I want to sample the device every 0.5 seconds - so I created a loop that repeats itself every 500miliseconds and works pretty well:

while(_bool)
{
    Sample_USB_Device();
    Present_Data_To_Screen();
}

My question is this:

How can I control the _bool variable? When I run the code the GUI freezes and I don't have any access to it. I tried to used Threads, but I can't send the data back from the thread to the GUI (or I don't know how).

gdoron
  • 147,333
  • 58
  • 291
  • 367
Trout
  • 83
  • 1
  • 8
  • 2
    Just search for "updating gui from background threads" – L.B Mar 22 '12 at 15:52
  • LB has it, you need a backgroundworker http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx – deltree Mar 22 '12 at 15:53
  • What GUI framework are you using? WinForms, WPF, or something else? – Mark Byers Mar 22 '12 at 15:54
  • possible duplicate of [Update Winforms UI from background thread result](http://stackoverflow.com/questions/983289/update-winforms-ui-from-background-thread-result) – L.B Mar 22 '12 at 15:54
  • 2
    Tip: `_bool` is a very bad name for a variable. It's better to name a variable based on what the data represents, than how the data is represented. For example, call it `continueSampling`. – tcovo Mar 22 '12 at 15:57
  • 1
    _bool is just an example, its not the actual code, but thanks for the advise. Thanks for all the quick responses! – Trout Mar 22 '12 at 16:00

6 Answers6

7

You can use a Timer to run your code at a specified interval, instead of using a loop. The timer can be enabled or disabled.

рüффп
  • 5,172
  • 34
  • 67
  • 113
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • That's awesome, I thought he was already using a timer for some reason. – ashes999 Mar 22 '12 at 15:55
  • That's a good solution if Sample_USB_Device() is quite fast. If it isn't, note that Sample_USB_Device is executed in the UI thread, i.e., the UI blocks during execution. – Heinzi Mar 22 '12 at 16:18
  • Sample_USB_Device() should return a result in about (1/50) seconds, so I think I'm good... – Trout Mar 22 '12 at 16:20
  • @Heinzi: If you use synchronous reads then it will block. If you use async reads with a timeout, then it doesn't need to block the UI thread. – Mark Byers Mar 22 '12 at 16:20
0

Using a Timer with C# 5.0 & TPL will allow you to invoke an async event handler which at the defined interval, which will poll the device and automagically marshall the result back onto the UI Thread.

public partial class Form1 : Form
{
    private readonly Timer _sampleTimer;

    public Form1()
    {
        InitializeComponent();

        _sampleTimer = new Timer
            {
                Interval = 500 // 0.5 Seconds
            };
        _sampleTimer.Tick += SampleUsb;
    }

    private async void SampleUsb(object sender, EventArgs e)
    {
        // Since we asynchronously wait, the UI thread is not blocked by "the work".
        var result = await SampleUsbDeviceAsync();

        // Since we resume on the UI context, we can directly access UI elements.
        resultTextField.Text = result;
    }

    private async Task<string> SampleUsbDeviceAsync()
    {
        await Task.Delay(1000); // Do actual work sampling usb async (not blocking ui)
        return DateTime.Now.Ticks.ToString(); // Sample Result
    }

    private void startButton_Click(object sender, EventArgs e)
    {
        _sampleTimer.Start();
    }

    private void stopButton_Click(object sender, EventArgs e)
    {
        _sampleTimer.Stop();
    }
рüффп
  • 5,172
  • 34
  • 67
  • 113
holsee
  • 1,974
  • 2
  • 27
  • 43
0

Use threading:

public class ThreadExample {
    private bool _running;
    public void start() {
       Thread t = new Thread(doStuff);
       _running = true;
       t.Start();
    }

    public void stop() {
       _running = false;
    }

    public void doStuff() {
        while(_running){
            Sample_USB_Device();
            Present_Data_To_Screen();
            Thread.Sleep(500);
        } 
    }
}
Paul Grimshaw
  • 19,894
  • 6
  • 40
  • 59
  • Present_Data_To_Screen will probably not be able to directly update UI elements, since it's running in a background thread. – Heinzi Mar 22 '12 at 16:02
0
  • You may be able to use a timer to fire an event for polling, but that can be limiting
  • You could run your poll in another thread but remember that you cannot update the gui except on the thread that created it ... use Invoke to do the updates in a gui safe manner
  • A threadsafe way to do your value on whether or not to continue your loop is to use the Interlocked.Read and Interlocked.Increment methods.
jtm001
  • 371
  • 2
  • 3
0

Here, use a backgrounder.

backgroundWorker1.RunWorkerAsync(); // Start the backgroundworker.

private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    // Do you stuff here.
}

private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
    // Report the changes.
    // NOTE: backgroundWorker1.WorkerReportsProgress = true; has to be true.
}

private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
    // Do something when we are done.
}

EDIT: This only stops your freezing problem.

Yuki Kutsuya
  • 3,968
  • 11
  • 46
  • 63
0

Use a BackgroundWorker to execute your loop in the background. This will ensure that your UI stays responsive:

BackgroundWorker bw;

public void StartBackgroundLoop() {
    bw = new BackgroundWorker();
    bw.WorkerSupportsCancellation = true;
    bw.WorkerReportsProgress = true;

    bw.DoWork += (sender, e) => {
        // this will happen in a background thread
        while (!bw.CancellationPending) {
            data = Sample_USB_Device();
            bw.ReportProgress(0, data);
        }
    }

    // this event is triggered *in the UI thread* by bw.ReportProgress
    bw.ProgressChanged += (sender, e) => {
        Data data = (Data)e.UserState;  // this is the object passed to ReportProgress
        Present_Data_To_Screen(data);
    }

    bw.RunWorkerAsync(); // starts the background worker
}

public void StartBackgroundLoop() {
    bw.CancelAsync();     // sets bw.CancellationPending to true
}
рüффп
  • 5,172
  • 34
  • 67
  • 113
Heinzi
  • 167,459
  • 57
  • 363
  • 519