C#解决窗体冷却的invoke用例

C#解决窗体冷却的invoke用例

主要参考

  1. http://blog.sina.com.cn/s/blog_621e24e201015r29.html
  2. https://www.cnblogs.com/nsky/p/4436309.html

在做GUI编程时,如果某次动作的运算时间非常长,比如要对大量数据进行加载,或者执行一个运算量很大的计算,或是socket编程在网络速度不理想的时候,窗体会停住无法响应,直到加载完成或运算结束,这对GUI操作来说是不太合适的。比如从外部相机设备读取图像数据时,图片有2048*2048像素*2字节的大小,并且要连续进行加载,虽然图像还在显示,但是整个程序都被加载图像占用,其它的控件几乎无法正常使用。最近在做的一个程序在调用外部批处理,批处理的执行结果显示在程序界面。自批处理调用之始到批处理进行完,程序都会处在无法操作的假死状态。

为了解决这个问题,就要掌握C#中invoke和delegate的特性。

先看主要过程

private void button1_Click(object sender, EventArgs e)
{
    richTextBox3.Text = "running..,";

    //开始执行批处理,这里往往耗时较长
    String output = "";
    RunCmd("test.bat", out output);
    String[] res = output.Split('\n');
    
    //批处理执行完毕, 将内容更新到richTextBox3
    richTextBox3.Text = "";
    for (int i = 4; i < res.Length; i++)
    {
        this.richTextBox3.Text += res[i];
    }

}

点击button1之后,先让文本框显示”running…”的文字,然后去执行批处理,这时窗体会冷却直到运行完成。这是线程之间出现占用现象,我们启动一个新的线程进行这些操作

private void button1_Click(object sender, EventArgs e)
{
    richTextBox3.Text = "compiling...";
    new Thread(
    () =>
    {
        String output = "";
        RunCmd("tst.bat", out output);
        String[] res = output.Split('\n');
        richTextBox3.Text = "";
        for (int i = 4; i < res.Length; i++)
        {
                this.richTextBox3.Text += res[i];
        }

    }
    ).Start();
}

可以发现,点击button1之后窗体不再冷却,是可以对其它控件进行操作的。但是突然跳出来 “线程间操作无效: 从不是创建控件“richTextBox3”的线程访问它。”的异常。查阅相关材料发现,是因为官方认为是当有多个并发线程尝试对UI进行读写时,容易造成线程争用资源带来的死锁。所以,默认不允许以非UI线程访问控件。

但很多情况下我们又确实需要用异步线程对UI进行一些操作。这时就用到Control.Invoke和Control.BeginInvoke。我们用Action<>把原来的语句包装成lambda函数,需要的时候就用invoke调这个lambda函数,就可以解决“不是创建控件*的线程访问”的异常。修改如下:

private void button1_Click(object sender, EventArgs e)
{
    richTextBox3.Text = "compiling...";
    new Thread(
    () =>
    {
        String output = "";
        RunCmd("tst.bat", out output);
        String[] res = output.Split('\n');
        
        Action<int> setNullForRT3  = (data) => richTextBox3.Text = "";
        Invoke(setNullForRT3, 0);

        for (int i = 4; i < res.Length; i++)
        {
            Action<int> act1 = (data) =>
            {
                this.richTextBox3.Text += res[i];
            };
            Invoke(act1, i);
        }

    }).Start();
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注