LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

C# WinForms中Invoke调用的优化思考

admin
2024年11月7日 8:56 本文热度 440

这也是一个网友提出这个问题,细想来还是可以优化一下,算是再熟悉明确一下这个吧。在 WinForms 开发中,跨线程更新 UI 是一个常见的场景。通常我们会使用 Control.Invoke 或 Control.BeginInvoke 来确保 UI 更新在正确的线程上执行。但是,如果使用不当,这些调用可能会带来性能问题。让我们深入探讨这个话题。

问题描述

让我们先看一个典型的场景 - 进度条更新:

public partial class Form1 : Form{    private void btnStart_Click(object sender, EventArgs e)    {        Task.Run(() =>        {            for (int i = 0; i <= 100; i++)            {                Thread.Sleep(50); // 模拟耗时操作                UpdateProgressBar(i);            }        });    }
   private void UpdateProgressBar(int value)    {        if (progressBar1.InvokeRequired)        {            progressBar1.Invoke(new Action<int>(UpdateProgressBar), value);        }        else        {            progressBar1.Value = value;        }    }}

这段代码存在以下问题:

  1. 每次调用都创建新的 Action<int> 委托对象

  2. 频繁的跨线程调用可能导致UI响应迟钝

  3. 同步调用 Invoke 会阻塞工作线程

优化方案

1. 缓存委托对象

第一个简单的优化是缓存委托对象:

public partial class Form1 : Form{    private readonly Action<int> _updateProgressBarAction;
   public Form1()    {        InitializeComponent();        _updateProgressBarAction = new Action<int>(UpdateProgressBar);    }
   private void btnStart_Click(object sender, EventArgs e)    {        Task.Run(() =>        {            for (int i = 0; i <= 100; i++)            {                Thread.Sleep(50);                UpdateProgressBar(i);            }        });    }
   private void UpdateProgressBar(int value)    {        if (progressBar1.InvokeRequired)        {            progressBar1.Invoke(_updateProgressBarAction, value);        }        else        {            progressBar1.Value = value;        }    }}

2. 使用 Progress<T>

更现代的方式是使用 Progress<T> 类:

public partial class Form1 : Form{    private readonly IProgress<int> _progress;
   public Form1()    {        InitializeComponent();        _progress = new Progress<int>(value => progressBar1.Value = value);    }
   private async void btnStart_Click(object sender, EventArgs e)    {        await Task.Run(() =>        {            for (int i = 0; i <= 100; i++)            {                Thread.Sleep(50);                _progress.Report(i);            }        });    }}

3. 批量更新策略

如果更新频率过高,可以采用批量更新策略:

public partial class Form1 : Form{    private const int UpdateThreshold = 5; // 每5%更新一次
   private async void btnStart_Click(object sender, EventArgs e)    {        var progress = new Progress<int>(value => progressBar1.Value = value);
       await Task.Run(() =>        {            for (int i = 0; i <= 100; i++)            {                Thread.Sleep(50);                if (i % UpdateThreshold == 0)                {                    ((IProgress<int>)progress).Report(i);                }            }        });    }}

4. 使用 BeginInvoke 异步调用

如果不需要等待UI更新完成,可以使用 BeginInvoke

public partial class Form1 : Form{    private readonly Action<int> _updateProgressBarAction;
   public Form1()    {        InitializeComponent();        _updateProgressBarAction = new Action<int>(UpdateProgressBarAsync);    }
   private void btnStart_Click(object sender, EventArgs e)    {        Task.Run(() =>        {            for (int i = 0; i <= 100; i++)            {                Thread.Sleep(50);                UpdateProgressBarAsync(i);            }        });    }
   private void UpdateProgressBarAsync(int value)    {        if (progressBar1.InvokeRequired)        {            progressBar1.BeginInvoke(_updateProgressBarAction, value);        }        else        {            progressBar1.Value = value;        }    }}

5. 综合示例:带取消和异常处理的进度更新

下面是一个更完整的示例,包含了错误处理、取消操作和进度更新:

// 进度信息类  public class ProgressInfo{    public int Percentage { get; set; }    public string Message { get; set; }}public partial class Form1 : Form{    private CancellationTokenSource _cts;    private readonly IProgress<ProgressInfo> _progress;    private bool _isRunning;
   public Form1()    {        InitializeComponent();        // 初始化进度报告器          _progress = new Progress<ProgressInfo>(OnProgressChanged);        InitializeControls();    }    private void InitializeControls()    {        // 初始状态设置          btnCancel.Enabled = false;        progressBar1.Minimum = 0;        progressBar1.Maximum = 100;        progressBar1.Value = 0;    }
   private void OnProgressChanged(ProgressInfo info)    {        progressBar1.Value = info.Percentage;        lblStatus.Text = info.Message;    }
   private async void btnStart_Click(object sender, EventArgs e)    {        if (_isRunning)            return;
       try        {            _isRunning = true;            UpdateUIState(true);
           // 创建新的取消令牌源              _cts = new CancellationTokenSource();
           // 执行长时间运行的任务              await ProcessLongRunningTaskAsync(_cts.Token);
           MessageBox.Show("处理完成!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);        }        catch (OperationCanceledException)        {            MessageBox.Show("操作已被用户取消", "已取消", MessageBoxButtons.OK, MessageBoxIcon.Information);        }        catch (Exception ex)        {            MessageBox.Show($"处理过程中发生错误:{ex.Message}", "错误",                          MessageBoxButtons.OK, MessageBoxIcon.Error);        }        finally        {            _isRunning = false;            UpdateUIState(false);            _cts?.Dispose();            _cts = null;        }    }
   private void UpdateUIState(bool isProcessing)    {        btnStart.Enabled = !isProcessing;        btnCancel.Enabled = isProcessing;    }
   private async Task ProcessLongRunningTaskAsync(CancellationToken token)    {        // 模拟一个需要处理100个项目的长时间运行任务          const int totalItems = 100;
       await Task.Run(async () =>        {            try            {                for (int i = 0; i <= totalItems; i++)                {                    // 检查是否请求取消                      token.ThrowIfCancellationRequested();
                   // 模拟处理工作                      await Task.Delay(50, token);
                   // 每处理一个项目报告进度                      if (i % 5 == 0)                    {                        _progress.Report(new ProgressInfo                        {                            Percentage = i,                            Message = $"正在处理... {i}%"                        });                    }                }
               // 报告完成                  _progress.Report(new ProgressInfo                {                    Percentage = 100,                    Message = "处理完成"                });            }            catch (Exception)            {                // 确保在发生异常时更新UI显示                  _progress.Report(new ProgressInfo                {                    Percentage = 0,                    Message = "操作已取消"                });                throw; // 重新抛出异常,让外层处理              }        }, token);    }
   private void btnCancel_Click(object sender, EventArgs e)    {        if (_cts?.IsCancellationRequested == false)        {            // 显示确认对话框              if (MessageBox.Show("确定要取消当前操作吗?", "确认取消",                              MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)            {                _cts?.Cancel();                lblStatus.Text = "正在取消...";                btnCancel.Enabled = false;            }        }    }
   // 防止内存泄漏      protected override void OnFormClosing(FormClosingEventArgs e)    {        if (_isRunning)        {            e.Cancel = true;            MessageBox.Show("请等待当前操作完成或取消后再关闭窗口", "提示",                          MessageBoxButtons.OK, MessageBoxIcon.Warning);            return;        }
       _cts?.Dispose();        base.OnFormClosing(e);    }}

总结

在 WinForms 应用程序中,正确处理跨线程UI更新是很重要的。通过采用适当的模式和实践,我们可以:

  1. 减少不必要的对象创建

  2. 提高应用程序的响应性

  3. 使代码更加清晰和易维护

  4. 避免潜在的内存问题

  5. 提供更好的用户体验


选择哪种方式取决于具体的应用场景,但总的来说,使用 API(如 Progress<T> 和 async/await)通常是更好的选择。对于需要精细控制的场景,可以考虑使用缓存的委托对象和自定义的更新策略。


该文章在 2024/11/7 10:30:53 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved