簡體   English   中英

如何防止 backgroundWorker 導致 UI 變得遲緩?

[英]How can I prevent the backgroundWorker causing the UI to become sluggish?

我制作了一個 C# WinForms 應用程序,其中我使用圖表 plot 數千個實時數據點。 我注意到,在我的應用程序運行期間,當我打開一個網絡瀏覽器時,plot 會凍結。 我試圖 plot 少點,但似乎永遠不知道將並行執行哪個程序,所以我擔心其他程序的 CPU 使用率取決於 PC 會影響性能。

編輯:

        private void button1_Click(object sender, EventArgs e)
        {
///


            _cts = new CancellationTokenSource();
            _infiniteLoop = InfiniteLoop(_cts.Token);



}




        private async Task InfiniteLoop(CancellationToken cancellationToken = default)
        {
            ushort[] ushortArray = null;
            while (true)
            {
                Task loopMinimumDurationTask = Task.Delay(100, cancellationToken);
                Task<ushort []> calculationTask = Task.Run(() => Calculate());
                if (ushortArray != null) PlotData(ushortArray);
                ushortArray = await calculationTask;
                await loopMinimumDurationTask;
            }
        }

        public  ushort [] Calculate()
        {
            init();
            daq.ALoadQueue(chArray, chRange, CHANCOUNT);

            ScanOptions options = ScanOptions.Background | ScanOptions.Continuous | ScanOptions.ConvertData;
            //setup the acquisiton
            UL = daq.AInScan(FIRSTCHANNEL, SINGLE_KANAL_NUM, BUFFERSIZE, ref Rate, Range.Bip10Volts, buffer, options);
            UL = daq.GetStatus(out daqStatus, out Count, out Index, FunctionType.AiFunction);


            if ((Index >= HALFBUFFSIZE) & ReadLower) //check for 50% more data
            {
                //get lower half of buffer
                UL = MccService.WinBufToArray(buffer, ushortArray, 0, HALFBUFFSIZE);
                ReadLower = false; //flag that controls the next read

      

                return ushortArray;

            }

            else if ((Index < HALFBUFFSIZE) & !ReadLower)
            {
                //get the upper half
                UL = MccService.WinBufToArray(buffer, ushortArray, HALFBUFFSIZE, HALFBUFFSIZE);
                ReadLower = true;//flag that controls the next read

      

                return ushortArray;

            }

            return null;
        }


        public void PlotData(ushort[] datArray_Plot)
        {

            ////////Thread.Sleep(10);
            SerialList1.Clear();

            for (int b = 0; b < HALFBUFFSIZE; b++)
            {
                UL = (daq.ToEngUnits(Range.Bip10Volts, datArray_Plot[b], out temp2));
                SerialList1.Add(temp2);
                SerialList2.Add(temp2);
                ikb_p = ikb_p + 1;
            }

            int out_size = SerialList1.Count / h; //size of downsampled array

            if (out_size <= 2)
                out_size = 2;

            array = SerialList1.ToArray(); //original array

            if (h != 1)
                array = Downsample(array, out_size); //downsampled array

            if (ikb_p > BUFFERSIZE)
            {

                chart1.Series["Ch0"].Points.SuspendUpdates();
                for (int b = 0; b < out_size; b++)
                {
                    chart1.Series["Ch0"].Points.AddY(array[b]); //Plots each sample or  use chart1.Series["Ch0"].Points.DataBindY(array);

                    if (chart1.Series["Ch0"].Points.Count > display_seconds * FREQ / h)
                    {
                        chart1.Series["Ch0"].Points.RemoveAt(0);
                    }

                }

                //chart1.Series["Ch0"].Points.ResumeUpdates();
                chart1.Invalidate();

            }




            //FFT
            if (SerialList2.Count > 4 * HALFBUFFSIZE / CHANCOUNT)
            {
                chart2.Series["Freq"].Points.Clear();
                float sampling_freq = (float)FREQ;
                float[] data = SerialList2.ToArray();

                double[] dftIn = new double[data.Length];
                double[] dftInIm = new double[data.Length];
                double[] DftIn = new double[data.Length];
                double[] FFTResult = new double[data.Length];
                double[] f = new double[data.Length];
                double[] power = new double[data.Length];

                double[] window = MathNet.Numerics.Window.Hamming(data.Length);

                for (int i = 0; i < data.Length; i++)
                {
                    dftIn[i] = window[i] * (double)data[i];
                }

                for (int i = 0; i < data.Length; i++)
                {
                    dftInIm[i] = 0.0;
                }

                FFT(dftIn, dftInIm, out reFFT, out imFFT, (int)Math.Log(data.Length, 2));

                for (int i = 0; i < data.Length / 2; i++)
                {
                    if (i > 0)
                    {
                        float a = sampling_freq / (float)data.Length;
                        float x = (float)i * a;
                        double y = Math.Sqrt(reFFT[i] * reFFT[i] + imFFT[i] * imFFT[i]);

                        f[i] = x;
                        FFTResult[i] = 2 * y / (data.Length / 2);

                        power[i] = 0.5 * FFTResult[i] * FFTResult[i];
                    }
                }

                double scale = data.Length / sampling_freq;

                chart2.Series["Freq"].Points.DataBindXY(f, power);

                float stdCh0 = 0;
                float avg1 = SerialList2.Average();
                float max1 = SerialList2.Max();
                float min1 = SerialList2.Min();
                float sum1 = (float)SerialList2.Sum(d => Math.Pow(d - avg1, 2));
                stdCh0 = (float)Math.Sqrt((sum1) / (SerialList2.Count() - 1));

                label5.Text = avg1.ToString("0.000000");
                label22.Text = stdCh0.ToString("0.000000");
                label70.Text = max1.ToString("0.000000");
                label61.Text = min1.ToString("0.000000");

                SerialList2.Clear();
                label1.Text = count_sample.ToString();

            }

            ///progressBar1
            double ratio = (double)count_sample / (seconds * FREQ);
            if (ratio > 1.000)
                ratio = 1;
            progressBar1.Value = (Convert.ToInt32(1000 * ratio));
            progressBar1.Invalidate();
            progressBar1.Update();

            //Display event handlers
            if (comboBox2_changed == true)
            {
                if (comboBox2.SelectedIndex == 0)
                {
                    //chart1.ChartAreas[0].RecalculateAxesScale();
                    chart1.ChartAreas[0].AxisY.IsStartedFromZero = false;
                }
                if (comboBox2.SelectedIndex == 1)
                {
                    //chart1.ChartAreas[0].RecalculateAxesScale();
                    chart1.ChartAreas[0].AxisY.IsStartedFromZero = true;
                }
                comboBox2_changed = false;
            }

            if (comboBox1_changed == true)
            {
                if (comboBox1.SelectedIndex == 0)
                {
                    chart1.Series["Ch0"].ChartType = SeriesChartType.FastLine;
                }
                else
                    chart1.Series["Ch0"].ChartType = SeriesChartType.FastPoint;
            }

            if (num_updown1_changed)
            {
                display_seconds = (float)numericUpDown1.Value * 0.001f;
                h = (int)numericUpDown2.Value;
                chart1.Series["Ch0"].Points.Clear();
                //chart1.ChartAreas[0].AxisX.Maximum = display_seconds * FREQ / h;
                num_updown1_changed = false;

                int avg = (int)((double)FREQ * (Decimal.ToDouble(numericUpDown1.Value) / 1000.0) / max_chart_points);
                if (avg != 0)
                    numericUpDown2.Value = avg;
            }

            if (num_updown2_changed)
            {
                display_seconds = (float)numericUpDown1.Value * 0.001f;
                h = (int)numericUpDown2.Value;
                chart1.Series["Ch0"].Points.Clear();
                //chart1.ChartAreas[0].AxisX.Maximum = display_seconds * FREQ / h;
                num_updown2_changed = false;

            }

        }



        private void Form_FormClosing(object sender, FormClosingEventArgs e)
        {
            _cts.Cancel();
            // Wait the completion of the loop before closing the form
            try { _infiniteLoop.GetAwaiter().GetResult(); }
            catch (OperationCanceledException) { } // Ignore this error
        }

您可以使用threadpriority

Thread.CurrentThread.Priority = ThreadPriority.Highest;

然而,在大多數情況下,這被認為是糟糕的形式,因為操作系統處於更好的 position 來決定哪個程序值得 CPU 時間。 即使您明確要求,它也不需要更多時間來遵循您的請求。

如果繪圖需要相當長的時間,您可能會考慮:

  • 你能以某種方式優化繪圖嗎?
  • 你能減少點數嗎?
    • 你也許可以 plot 數據集的一小部分?
    • 您可以預處理 plot 以降低點密度。 屏幕通常具有 2k-4k 的分辨率,因此如果您有一個包含更多點的折線圖,用戶無論如何都無法看到它。

我的建議是廢棄過時BackgroundWorker ,轉而采用無限異步循環。 下面的示例假定存在應在后台線程上運行並應返回一個計算結果的Calculate方法,以及應在 UI 線程上運行並應使用此結果的UpdateUI方法。

private async Task InfiniteLoop(CancellationToken cancellationToken = default)
{
    object calculationResult = null;
    while (true)
    {
        Task loopMinimumDurationTask = Task.Delay(100, cancellationToken);
        Task<object> calculationTask = Task.Run(() => Calculate());
        if (calculationResult != null) UpdateUI(calculationResult);
        calculationResult = await calculationTask;
        await loopMinimumDurationTask;
    }
}

這種設計具有以下特點:

  1. CalculateUpdateUI方法是並行工作的。
  2. 如果Calculate首先完成,它會等待UpdateUI完成,然后再開始下一次計算。
  3. 如果UpdateUI先完成,它會等待Calculate完成,然后再開始 UI 的下一次更新。
  4. 如果CalculateUpdateUI都在 100 毫秒內完成,則會施加額外的異步延遲,因此每秒不會發生超過 10 個循環。
  5. 可以通過取消可選的CancellationToken來終止無限循環。

上例中calculationResult結果變量的object類型僅用於演示。 除非計算的結果是微不足道的,否則您應該創建一個 class 或結構,以存儲每次循環更新 UI 所需的所有數據。 通過消除所有全局 state,您可以最大限度地減少 go 錯誤的數量。

使用示例:

private CancellationTokenSource _cts;
private Task _infiniteLoop;

private void Form_Load(object sender, EventArgs e)
{
    _cts = new CancellationTokenSource();
    _infiniteLoop = InfiniteLoop(_cts.Token);
}

private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
    _cts.Cancel();
    // Wait the completion of the loop before closing the form
    try { _infiniteLoop.GetAwaiter().GetResult(); }
    catch (OperationCanceledException) { } // Ignore this error
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM