惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
V2EX
C
Check Point Blog
博客园_首页
B
Blog
D
Docker
U
Unit 42
量子位
I
InfoQ
有赞技术团队
有赞技术团队
Martin Fowler
Martin Fowler
GbyAI
GbyAI
L
LangChain Blog
云风的 BLOG
云风的 BLOG
博客园 - Franky
美团技术团队
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
Vercel News
Vercel News
Recent Announcements
Recent Announcements
雷峰网
雷峰网
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Google DeepMind News
Google DeepMind News

博客园 - 萧萧空间

Windows 2003,XP安装Windows Phone 7 从海量数据中找出中位数(转) 界面开发(五)--- 界面优化 界面开发(四)--- 还窗体的新面貌 界面开发(三)--- 设置窗体的Region 界面开发(二)--- NativeWindow 界面开发(一)--- Hook所有的窗体 界面开发概述 ExtJs扩展之GroupPropertyGrid ExtJs之带图片的下拉列表框 寻找第K大的数的方法总结 我自己的ColorSpy Office 2010 beta版安装 Java反编译工具JD ExtJs Grid Drag Drap Splash窗体(ProgressWindowForm修改) 异步窗体实现操作进度(ProgressWindow) 线程封装组件(BackgroundWorker)和线程(Thread) NSIS 的使用心得
多线程开发
萧萧空间 · 2009-11-19 · via 博客园 - 萧萧空间

      前不久写了一个项目,其中需要处理大量的数据,这些数据都是记录型的,对这些数据进行处理是一个非常耗时的事情,.net在处理这些使用的过程中,就好像死机一样一动不动。这样给用户的感觉非常的不好。例如如下的例子,我们在一个窗体上放置一个ProgressBar,然后对ProgressBar进行循环添加,例如:

 1         private void btnProcess_Click(object sender, EventArgs e)
 2         {
 3             int nStart = Convert.ToInt32(txtStart.Text);
 4             int nEnd = Convert.ToInt32(txtEnd.Text);
 5 
 6             prgProcess.Minimum = nStart;
 7             prgProcess.Maximum = nEnd;
 8             for (int nValue = nStart; nValue <= nEnd; nValue++)
 9             {
10                 lblStatus.Text = "Processing item: " + Convert.ToString(nValue);
11                 prgProcess.Value = nValue;
12             } } 

      这段代码运行时正常的,但是对于数据量很大的时候,比如txtEnd.Text设置成1000000,这个时候界面上的进度条也许就是个固定值,一动不动,而且lblStatus.Text的现实也是为空,这是很正常的。程序就如同死机一样。当然,我们可以在循环中添加Application.DoEvents();使系统在空闲的时间处理其他的事情,这样系统的效果会好些。例如:

 1 private void btnProcess_Click(object sender, EventArgs e)
 2         {
 3             int nStart = Convert.ToInt32(txtStart.Text);
 4             int nEnd = Convert.ToInt32(txtEnd.Text);
 5 
 6             prgProcess.Minimum = nStart;
 7             prgProcess.Maximum = nEnd;
 8             for (int nValue = nStart; nValue <= nEnd; nValue++)
 9             {
10                 lblStatus.Text = "Processing item: " + Convert.ToString(nValue);
11                 prgProcess.Value = nValue;
12 
13                 Application.DoEvents();
14             }
15         }

      但这些不是本文的重点,本文的重点是使用多线程的的方式去处理这些内容,我们将在此窗体中添加Tread去处理这些复杂的操作。这样的效果会比Application.DoEvents();更好。

      还是在原来的窗体上,我们使用线程的方式去处理。

 1         delegate void DelegateType(int x);
 2         DelegateType TheDelegate;
 3 
 4         int StartFrom, EndTo;
 5 
 6         private void btnStartThreaded_Click(object sender, EventArgs e)
 7         {
 8             TheDelegate = MessageHandler;
 9 
10             StartFrom = Convert.ToInt32(txtStart.Text);
11             EndTo = Convert.ToInt32(txtEnd.Text);
12 
13             prgProcess.Minimum = StartFrom;
14             prgProcess.Maximum = EndTo;
15 
16             btnStartThreaded.Enabled = false;
17 
18             Thread MyThread = new Thread(ProcessRoutine);
19             MyThread.Start();
20         }
21 
22         void MessageHandler(int nProgress)
23         {
24             lblStatus.Text = "Processing item: " + Convert.ToString(nProgress);
25             prgProcess.Value = nProgress;
26         }
27 
28         void ProcessRoutine()
29         { 
30             for (int nValue = StartFrom; nValue <= EndTo; nValue++)
31             {
32                 this.Invoke(this.TheDelegate, nValue);
33             }

34         }

       这个是使用线程的方法去处理,但是问题还是有的,虽然线程处理了用户界面效果的问题,但是对于大数据量处理过程中,很耗时的,用户可能会去取消自己的操作,所以我们要添加方法去对自己的线程取消。微软这个方面做的很好,它提供了ManualResetEvent的委托类,可以轻松的对线程进行取消。代码如下:

       首先添加ManualResetEvent的委托:

   ManualResetEvent CancelEvent = new ManualResetEvent(false); 

        private void btnCancelButton_Click(object sender, EventArgs e)
        {
            btnCancelButton.Enabled 
= false;

            CancelEvent.Set();
            MyThread.Join();

            lblStatus.Text 

= "Cancelled!";

        } 

        修改ProcessRoutine的代码如下:

   void ProcessRoutine()
        {
            
for (int nValue = StartFrom; nValue <= EndTo; nValue++)
            {

                if (CancelEvent.WaitOne(0false== true)
                {
                    
return;
                }
                
this.BeginInvoke(this.TheDelegate, nValue);
                Thread.Sleep(
200);
            }
        }

       另外一点,我们还可以对线程进行扩充,就是将线程的暂停和继续,这个就很简单了,就是对线程的Suspend和Resume,只要再添加按钮设置线程的Suspend和Resume就可以了。代码如下:

        bool IsThreadPaused;
        private void btnPause_Click(object sender, EventArgs e)
        {
            
if (!IsThreadPaused)
            {
                IsThreadPaused 
= true;
                MyThread.Suspend();
                btnPause.Text 
= "Resume";
                lblStatus.Text 
= "Paused!";
                btnCancelButton.Enabled 
= false;
            }
            
else
            {
                IsThreadPaused 
= false;
                MyThread.Resume();
                btnPause.Text 
= "Pause";
                btnCancelButton.Enabled 
= true;
            }
        }