【问题标题】:How to handle an Inifinite Loop in C#如何在 C# 中处理无限循环
【发布时间】:2026-02-04 01:35:01
【问题描述】:

我有一个小程序,它应该从通过 USB 连接的设备中采样一些值。 我想每 0.5 秒对设备进行一次采样 - 所以我创建了一个循环,它每 500 毫秒重复一次并且效果很好:

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

我的问题是这样的:

如何控制_bool 变量?当我运行代码时,GUI 冻结,我无权访问它。我尝试使用线程,但我无法将数据从线程发送回 GUI(或者我不知道如何)。

【问题讨论】:

  • 只搜索“从后台线程更新 gui”
  • LB有,需要后台工作人员msdn.microsoft.com/en-us/library/…
  • 您使用的是什么 GUI 框架? WinForms、WPF 还是其他?
  • 提示:_bool 是一个非常糟糕的变量名称。最好根据数据表示的内容来命名变量,而不是根据数据的表示方式。例如,称之为continueSampling
  • _bool 只是一个示例,它不是实际代码,但感谢您的建议。感谢所有快速回复!

标签: c# infinite-loop


【解决方案1】:

您可以使用Timer 以指定的时间间隔运行您的代码,而不是使用循环。可以启用或禁用计时器。

【讨论】:

  • 太棒了,我以为他出于某种原因已经在使用计时器了。
  • 如果 Sample_USB_Device() 非常快,这是一个很好的解决方案。如果不是,请注意 Sample_USB_Device 在 UI 线程中执行,即在执行期间 UI 阻塞。
  • Sample_USB_Device() 应该会在大约 (1/50) 秒内返回结果,所以我觉得我很好......
  • @Heinzi:如果您使用同步读取,那么它将阻塞。如果您使用带有超时的异步读取,则不需要阻塞 UI 线程。
【解决方案2】:

使用线程:

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);
        } 
    }
}

【讨论】:

  • Present_Data_To_Screen 可能无法直接更新 UI 元素,因为它在后台线程中运行。
【解决方案3】:
  • 您可以使用计时器来触发事件进行轮询,但这可能会受到限制
  • 您可以在另一个线程中运行您的投票,但请记住,您无法更新 gui,除非在创建它的线程上...使用 Invoke 以 gui 安全的方式进行更新
  • 确定是否继续循环的线程安全方法是使用 Interlocked.Read 和 Interlocked.Increment 方法。

【讨论】:

    【解决方案4】:

    在这里,使用背景。

    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.
    }
    

    编辑:这只会阻止您的冻结问题。

    【讨论】:

      【解决方案5】:

      使用BackgroundWorker 在后台执行您的循环。这将确保您的 UI 保持响应:

      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
      }
      

      【讨论】:

        【解决方案6】:

        在 C# 5.0 和 TPL 中使用 Timer 将允许您调用异步事件处理程序,该处理程序会在定义的时间间隔内轮询设备并自动将结果编组回 UI 线程。

        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();
            }
        

        【讨论】: