前言
当有大量.NET程序员进入这个领域后,利用"即见即所得"的机制我们可以快速的开发出了很多应用程序,但是随着深入这个领域,我们会发现,有UI的设计往往不能开发出比较理想的系统原型,甚至有1~3年经验的.NET程序员仍然不能理解诸多概念,如多线程、反射、**机制,包括我本人也是如此。所以《.NET WinForm编程Q&A系列》就是基于本人的理解能力进行整理。整个系列都会包括如下内容:
【背景】重点提出WinForm编程中的问题以及相关晦涩的概念;
【概念】教科书或者百度百科关于这些问题以及概念的介绍,我估计大多数程序员不愿意接受这种方式;
【需求】对应这些问题以及概念,本人提出了一个系统实例,并希望借助这个实例可以对背景提出的若干问题进行求解,也让大家对概念进行一个梳理;
【实战】详细介绍实例的实现过程,当然针对实例,会有很多的解决方案,本文所提出的实战过程仅仅是其中一种。
【备注】欢迎大家提出宝贵意见。
背景
如何理解并应用多线程操作?
概念
1、多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的;
2、使用线程可以把占据时间长的程序中的任务放到后台去处理;
3、在本质上和结构来说,.NET是一个多线程的环境。有两种主要的多线程方法是.NET所提倡的:使用ThreadStart来开始你自己的进程,直接的(使用ThreadPool.QueueUserWorkItem)或者间接的(比如Stream.BeginRead,或者调用BeginInvoke)使用ThreadPool类。一般来说,你可以"手动"为长时间运行的任务创建一个新的线程,另外对于短时间运行的任务尤其是经常需要开始的那些,进程池是一个非常好的选择。进程池可以同时运行多个任务,还可以使用框架类。对于资源紧缺需要进行同步的情况来说,它可以限制某一时刻只允许一个线程访问资源。这种情况可以视为给线程实现了锁机制。线程的基类是System.Threading。所有线程通过CLI来进行管理。
需求
控件内容的定时变更,每隔1秒,文本控件将显示一个随机数(0,100),进度条进行显示。
如下图所示。
实战
1、根据需求中提到的系统要求,初级的程序员会快速的写出方案,如下所示:
[csharp]
private void button1_Click(object sender, EventArgs e)
{
while (true)
{
Random rd = new Random();
int num = rd.Next(0, 100);
this.textBox1.Text = num.ToString();
Thread.Sleep(1000);
}
}
2、一旦运行后,发现界面动不了,于是又考虑用计数器实现,如下:
[csharp]
private void button1_Click(object sender, EventArgs e)
{
this.timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
Random rd = new Random();
int num = rd.Next(0, 100);
this.textBox1.Text = num.ToString();
this.progressBar1.Value = num;
Thread.Sleep(1000);
}
3、此时F5后,发现系统正常运作了,这就是其中的一种解决方案,但是本文主要是要介绍多线程,所以我们考虑其他的步骤。
4、要使用多线程控制UI控件,必须用委托实现。调用控件的Invoke方法(Invoke方法的参数是一个委托类型的参数)。
具体步骤如下所示:
1.声明委托。
[csharp]
/// <summary>
/// 1.声明委托
/// </summary>
/// <param name="num"></param>
delegate void RefreshData(int num);
delegate void AutoRefresh();
2.声明委托处理函数(判断是主线程控制UI控件,还是Invoke(多线程)控制UI控件)。
[csharp]
/// <summary>
/// 2.声明委托处理函数refreshTextBox
/// </summary>
/// <param name="num"></param>
public void refreshTextBox(int num)
{
if (this.textBox1.InvokeRequired)
{
RefreshData rd = new RefreshData(refreshTextBox);
if (this.textBox1.IsHandleCreated)
{
this.textBox1.Invoke(rd, num);
}
}
else
{
this.textBox1.Text = num.ToString();
}
}
/// <summary>
/// 2.声明委托处理函数Display
/// </summary>
/// <param name="num"></param>
public void Display(int num)
{
if (this.progressBar1.InvokeRequired)
{
RefreshData rd = new RefreshData(Display);
if (this.progressBar1.IsHandleCreated)
{
this.progressBar1.Invoke(rd, num);
}
}
else
{
this.progressBar1.Value = num;
}
}
3.声明一个线程实例,将线程函数的委托传入ThreadStart()。
[csharp]
//3.声明一个线程实例
Thread refreshThread = new Thread(new ThreadStart(ar));
4.开启该线程。
[csharp]
//4.开启该线程
refreshThread.Start();
5.定义该线程函数,欲控制UI控件,则调用第2步的委托处理函数,他将自己判断选择用Invoke。
6.定义Invoke需要调用的函数
[csharp]
/// <summary>
/// 6.定义Invoke需要调用的函数
/// </summary>
private void AddAuto()
{
while (true)
{
Random rd = new Random();
int num = rd.Next(0, 100);
refreshTextBox(num);
Display(num);
}
}
备注
代码全集如下:
[csharp]
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace MulControlOp