多线程与UI操作

前言

  为了让程序尽快响应用户操作,在开发Winform应用程序时经常会使用多线程,对于耗时的操作如果不使用多线程将会使UI界面长时间处于停滞状态,这种情况是用户非常不愿意看到的,怎么办呢?用多线程。它可以很好的解决这个问题。下面是使用多线程操作界面UI的代码:

private void btnTest_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(Run);
    thread.Start();
}

private void Run()
{
    txtContent.Visible = false;
}

  从上面的代码中可以看出,我希望单击按钮将txtContent文本框给隐藏掉。可是,当项目启动时,代码却抛出System.InvalidOperationException异常,异常描述就是“线程间操作无效:从不是创建控件txtContent”的线程访问它,如图1-1所示。

这里写图片描述
图1-1 报错页面

问题原因

  之所以会出现这样的情况,是因为在.Net中做了限制,不允许在调试环境下使用线程访问并非它自己创建的UI控件,这么做可能是怕在多线程环境下对界面控件进行操作会出现不可预知的情况。

解决方案

  如果开发者可以确认自己的代码操作界面不会出现问题,可以用比较简单的办法解决,那就是设置CheckForIllegalCrossThreadCalls这个静态属性,它的默认是true,如果将其设为false的话,以后在多线程环境下操作界面再也不会抛出异常了,我们上面的代码可以修改为:

private void btnTest_Click(object sender, EventArgs e)
{
    //指示是否对错误线程的调用,即是否允许在创建UI的线程之外的线程访问
    CheckForIllegalCrossThreadCalls = false;
    Thread thread = new Thread(Run);
    thread.Start();
}

private void Run()
{
    txtContent.Visible = false;
}

  可是,毕竟微软做出这个限制也不是为了难为你,它的存在肯定是有道理的,上面的操作会允许程序中所有的创建控件之外的线程操作控件,这种方式可能会给项目带来未知的风险。那么,怎么降低这种风险呢?我们也可以设置某一个线程可以访问,而其他的还是不可以访问。

利用Invoke方法来操作界面
private void btnTest_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(Run);
    thread.Start();
}
private void Run()
{
    /*
     * InvokeRequired,获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用Invoke方法,
     * 因为调用方位于创建控件所在的线程以外的线程中。
     */
    if (InvokeRequired)
    {
        /*
         * Action,可以使用此委托以参数形式传递方法,而不用显式声明自定义的委托,
         * 封装的方法必须与此委托定义的方法签名相对应,
         * 也就是说,封装的方法不得具有参数,并且不得返回值,通常这种方法用于执行某个操作。
         */
        //delegate() { txtContent.Visible = false; },无参无返回值的匿名方法
        this.Invoke(new Action(delegate() { txtContent.Visible = false; }));
    }
    else
    {
        txtContent.Visible = false;
    }

}

  上面执行的操作是将文本框隐藏,而如果我们需要为其赋值的话,就需要有参数了,那么用Action就不可以了,只能显式定义一个委托来做,代码如下:

private delegate void SetName(string name);

private void btnTest_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(Run);
    thread.Start();
}

private void Run()
{
    if (InvokeRequired)
    {
        this.Invoke(new SetName(SetTextName), "jujianfei");
    }
    else
    {
        txtContent.Visible = false;
    }            
}

private void SetTextName(string name)
{
    txtContent.Text = name;
}

  另外,还可以用BackgroundWorker类操作界面,不过这里不做研究,有兴趣的可以看本篇博客的参考文章。

总结

  多线程编程任重道远,点滴积累方可修成正果。文章如有不当之处,还望不吝赐教,多谢。

参考文章:多线程系列4__周公

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值