现在的位置: 首页 > 综合 > 正文

如何在子线程中通过安全调用方法操作窗体控件

2013年02月20日 ⁄ 综合 ⁄ 共 3961字 ⁄ 字号 评论关闭
 在查询海量数据时,由于查询需要时间,因此要在查询的同时显示状态信息以通知用户当前正在进行的操作,通常采用多线程进行处理,但是初学者常常使用如下类似的代码:

        //button按钮的click事件处理函数
        private void buttonBookStat_Click(object sender, EventArgs e)
        {
            ThreadStart tds1 
= new ThreadStart(prompt);
            Thread td1 
= new Thread(tds1);
            td1.Priority 
= ThreadPriority.Normal;
            td1.Start();

            ThreadStart tds2 = new ThreadStart(showBooksInfo);
            Thread td2 
= new Thread(tds2);
            td2.Priority 
= ThreadPriority.Normal;
            td2.Start();
        }

        //显示提示
        private void prompt()
        {
            
this.labelPrompt.Text = "正在查询,请稍侯......";
        }

        //查询并绑定到窗体控件datagridview1
        private void showBooksInfo()
        {
            
string sql = "select .........";

            try
            {
                SqlDataAdapter da 
= new SqlDataAdapter(sql, conn);
                DataSet ds 
= new DataSet();
                da.Fill(ds, 
"table1");

                this.dataGridView1.AutoGenerateColumns = true;
                
this.dataGridView1.DataSource = ds;
                
this.dataGridView1.DataMember = "table1";

                this.labelPrompt.Text = "查询完毕";
            }
            
catch (SqlException er)
            {
                MessageBox.Show(er.Message);
            }
        }

这样的代码在子线程中操作窗体控件,盖茨大叔说是不安全的,但我用VS2005编译并不报错,也能正常运行,不过,运行时还是出了问题,在上述例子中datagridview控件的滚动条不能正常显示,无法使用滚动条滚动显示数据,看来,如果要在子线程中操作窗体控件,还是得采用线程安全调用的方式。要使用安全调用的方式,必须先定义一个委托,其签名与我们要操作控件的函数相同,步骤如下:

1.定义一个与窗体控件操作方法签名相同的委托

2.定义操作窗体控件的方法

3.定义一个安全调用方法,用2中的方法实例化我们定义的委托

4.在线程中调用3中的安全方法

因此,我们将上述代码作一修改,红色为新增和改动代码

       //定义委托
        delegate void mydelegate();

        //button按钮的click事件处理函数
        private void buttonBookStat_Click(object sender, EventArgs e)
        {
            ThreadStart tds1 
= new ThreadStart(promptSafe);
            Thread td1 
= new Thread(tds1);
            td1.Priority 
= ThreadPriority.Normal;
            td1.Start();

            ThreadStart tds2 = new ThreadStart(showBooksInfoSafe);
            Thread td2 
= new Thread(tds2);
            td2.Priority 
= ThreadPriority.Normal;
            td2.Start();
        }

        //显示提示
        private void prompt()
        {
            
this.labelPrompt.Text = "正在查询,请稍侯......";
        }

        //安全调用显示提示方法
        private void promptSafe()
        {
            
if (this.labelPrompt.InvokeRequired)
            {
                
                mydelegate showprompt = new mydelegate(prompt);
                
                this.Invoke(showprompt);
            }
            else

            {
                prompt();
            }
        }

        
//查询并绑定到窗体控件datagridview1
        private void showBooksInfo()
        {
            
string sql = "select .........";

            try
            {
                SqlDataAdapter da 
= new SqlDataAdapter(sql, conn);
                DataSet ds 
= new DataSet();
                da.Fill(ds, 
"table1");

                this.dataGridView1.AutoGenerateColumns = true;
                
this.dataGridView1.DataSource = ds;
                
this.dataGridView1.DataMember = "table1";

                this.labelPrompt.Text = "查询完毕";
            }
            
catch (SqlException er)
            {
                MessageBox.Show(er.Message);
            }
        }

        //安全调用查询
        private void showBooksInfoSafe()
        {
            
if (this.dataGridView1.InvokeRequired)
            {
                
//定义委托对象
                mydelegate showbooks = new mydelegate(showBooksInfo);
                
//因此线程占用大量CPU时间,因此让它分些CPU时间出来给其他线程
                Thread.Sleep(10);
                
//通过窗体的Invoke方法调用委托
                this.Invoke(showbooks);
            }
            
else

            {
                showBooksInfo();
            }
        }
else//通过窗体的Invoke方法调用委托//定义委托对象

再次编译运行,现在你会发现datagridview控件的滚动条又正常了,这就是安全调用的好处,它会解决一些莫名其妙出现的问题。

不过,我们解决了问题之后仍然存在疑问:到底这种不安全调用的方式问题出在哪?其底层的错误在什么地方?在上述具体问题中,不安全调用方式仍然可以给datagridview指定数据源并显示,但在滚动条那里出现问题,我们无法获知datagridview这个控件的底层实现方式,微软也只是笼统地说在线程中操作窗体控件需要安全方式,我还得继续查阅资料,继续思考,等有了收获再写出来吧。

抱歉!评论已关闭.