WinForm项目中使用控件较多时卡顿问题

与 UI 无关的处理应该单独、异步执行,然后当执行完毕之后才调用主线程来更新界面。例如测试下面的例子:

using System;
using System.Threading;
using System.Windows.Forms;
 
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        void button1_Click(object sender, EventArgs e)
        {
            button1.Text = "测试开始";
            Test();
        }
 
        void Test()
        {
            Thread.Sleep(10000);
            this.button1.Text = "测试完成";
        }
    }
}


当你点击按钮的时候,你会发现10秒钟之内界面卡死了,窗口根本用鼠标拖动不了!这就是很糟糕的程序。而改成

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        async void button1_Click(object sender, EventArgs e)
        {
            button1.Text = "测试开始";
            await Test();
            this.button1.Text = "测试完成";
        }
 
        Task Test()
        {
            return Task.Run(() =>   //把与UI刷新无关的语句放到这个匿名委托中执行
            {
                Thread.Sleep(10000);
            });
        }
    }
}


你会发现在耗时过程执行过程中也能自由地拖动窗口。 假设在异步并发过程中要修改窗口控件,那么需要使用委托来访问控件。例如:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        async void button1_Click(object sender, EventArgs e)
        {
            button1.Text = "测试开始";
            await Test();
            this.button1.Text = "测试完成";
        }
 
        Task Test()
        {
            return Task.Run(() =>   //把与UI刷新无关的语句放到这个匿名委托中执行
            {
                Thread.Sleep(2500);
                this.button1.BeginInvoke((Action)delegate
                {
                    this.button1.Text = "1";
                });
                Thread.Sleep(2500);
                this.button1.BeginInvoke((Action)delegate
                {
                    this.button1.Text = "2";
                });
                Thread.Sleep(2500);
                this.button1.BeginInvoke((Action)delegate
                {
                    this.button1.Text = "3";
                });
                Thread.Sleep(2500);
            });
        }
    }
}


这里,在耗时的异步过程执行过程中访问了 UI 界面控件,就需要使用 BeginInvoke 委托来操作控件,使用 BeginInvoke.Invoke 委托来操作控件,这是15年前就在 .net 中有的操作规范。

PS:

推荐的使用方法

了解到 ThreadPoolTaskScheduler 的默认行为之后,我们可以做这些事情来充分利用线程池带来的优势:

  1. 对于 IO 操作,尽量使用原生提供的 Async 方法,这些方法使用的是 IO 完成端口,占用线程池中的 IO 线程而不是普通线程(不要自己使用 Task.Run 占用线程池资源);
  2. 对于没有 Async 版本的 IO 操作,如果可能耗时很长,则指定 CreateOptions 为 LongRunning(这样便会直接开一个新线程,而不是使用线程池)。
  3. 其他短时间执行的任务才推荐使用 Task.Run。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值