加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

WinForm(C#)多线程编程并更新界面(UI)

(2013-01-18 20:34:52)
标签:

多线程

winform

invoke

委托

参数

it

分类: C#那些事儿
一个简单明了的例子
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel;  
  4. using System.Drawing;  
  5. using System.Linq;  
  6. using System.Windows.Forms;  
  7. using System.Threading;  
  8.   
  9. namespace doWorker  
  10.  
  11.     public partial class Form1 Form  
  12.      
  13.         delegate void MyDelegate(int value);  
  14.         Thread t;  
  15.         int 0;  
  16.         public Form1()  
  17.          
  18.             InitializeComponent();  
  19.          
  20.   
  21.         // 在新的线程中做“需要长时间做的”工作  
  22.         private void button1_Click(object sender, EventArgs e)  
  23.          
  24.             new Thread(doWork);  
  25.             t.Start();  
  26.          
  27.   
  28.         // 要长时间做的工作  
  29.         void doWork()  
  30.          
  31.             MyDelegate new MyDelegate(setValue);  
  32.             while (true 
  33.              
  34.                 ++i;  
  35.   
  36.                 //---WinForm--  
  37.                 this.Invoke(d, i);  
  38.                 //----WPF---added by wonsoft.cn---  
  39.                 this.Dispatcher.Invoke(d, i);  
  40.   
  41.                 Thread.Sleep(100);  
  42.              
  43.          
  44.   
  45.         // 更新用户界面  
  46.         void setValue(int value)      
  47.          
  48.             label1.Text value.ToString();  
  49.          
  50.   
  51.         // 终止线程的执行  
  52.         private void button2_Click(object sender, EventArgs e)  
  53.          
  54.             t.Abort();  
  55.          
  56.      

当我们利用一个新创建的线程来执行某些花时间的运算时,怎样知道运算进度如何并通过UI反映给用户呢?解决方法很多!比如熟悉多线程编程的用户很快会想到,我们采用一些低级的同步方法,工作者线程把状态保存到一个同步对象中,让UI线程轮询(Polling)该对象并反馈给用户就可以了。不过,这还是挺麻烦的,实际上不用这样做,Control类(及其派生类)对象有一个Invoke方法很特别,这是少数几个不受线程限制的成员之一。我们前面说到,绝对不要在任何其他线程里面调用非本线程创建的控件的成员时,也说了“只有极个别情况例外”,这个Invoke方法就是极个别情况之一----Invoke方法可以从任何线程里面调用。下面我们来讲解Invoke方法。

Invoke方法的参数很简单,一个委托,一个参数表(可选),而Invoke方法的主要功能就是帮助你在UI线程(即创建控件的线程)上调用委托所指定的方法。Invoke方法首先检查发出调用的线程(即当前线程)是不是UI线程,如果是,直接执行委托指向的方法,如果不是,它将切换到UI线程,然后执行委托指向的方法。不管当前线程是不是UI线程,Invoke都阻塞直到委托指向的方法执行完毕,然后切换回发出调用的线程(如果需要的话),返回。注意,使用Invoke方法时,UI线程不能处于阻塞状态。以下MSDN里关于Invoke方法的说明:

“控件上有四种方法可以安全地从任何线程进行调用:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。

说完Invoke,顺便说说BeginInvoke,毫无疑问这是Invoke的异步版本(Invoke是同步完成的),不过大家不要和上面的System.Windows.Forms.MethodInvoker委托中的BeginInvoke混淆,两者都是利用不同线程来完成工作,但是控件的BeginInvoke方法总是使用UI线程,而其他的异步委托调用方法则是利用线程池里的线程。相对Invoke而言,使用BeginInvoke稍稍麻烦一点,但还是那句话,异步比同步效果好,尽管复杂些。比如同步方法可能出现这样一种死锁情况:工作者线程通过Invoke同步调用UI线程里的方法时会阻塞,而万一UI线程正在等待工作者线程做某件事时怎么办?因此,能够使用异步方法时应尽量使用异步方法。

最近做一个程序时遇到了这样的问题,其中点击一个按钮之后需要进行长时间的处理工作,但是在处理过程中,UI就停止更新了(我这里是socket接收客户端消息显示在界面),而且处理完成后,才会更新,最可恶的是更新出来的竟然是几条一样的消息,且是最后发送的那条消息的内容。因此,需要多线程。

下面的代码是我更改之后的应用在我的程序里的,需改后可以满足我的需求了

        delegate void MyDelegate(int value);
        Thread processThread;
        int ii = 0;

 public void messageProcess(Object mess )
        {
            MyDelegate d = new MyDelegate(setValue);
            //string processedMess;
            StringBuilder procMess = new StringBuilder();
            string[] sbs = { "我们是美好的分,你们是很好的的", "大家不是大家分,我们却是我们个", "宝珠盛世辞,共和信息英", "不知不住的,方法改革额", "杨家墩分场是人,经济开发区分额" };
            for (int i = 0; i < sbs.Length - 1; i++)
            {
                procMess.Append(sbs[i]).Append("|");
            }
            procMess.Append(sbs[4]);
            sendtoMessage = procMess.ToString();
            Thread.Sleep(10000);
            Console.WriteLine("线程传递的参数是:{0}", mess);
            Console.WriteLine("处理后的对联是:{0}", sendtoMessage);
            this.Invoke(d, ii);     
        }

        private void nextProcessBUT_Click(object sender, EventArgs e)
        {
           
                proStatuTB.Text = "正在处理";
                socketClientProcess.Remove(currentProcessContext);
                // 在新的线程中做“需要长时间做的”工作
                processThread = new Thread(messageProcess);
                processThread.Start(receivedMessage);
        }

        // 更新用户界面
        void setValue(int value)
        {
            string[] sArray2 = sendtoMessage.Split('|');
            foreach (string item in sArray2)
            {
                Console.WriteLine("处理后的条目是:{0}", item);
                proResultLB.Items.Add(item);
            }
            processBUT.Enabled = false;
            proStatuTB.Text = "处理完成";
            sendBUT.Enabled = true;
            processThread.Abort();
        }





0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有