多线程在VisualC#网络编程中的应用网络应用程序的一般都会或多或少的使用到线程,甚至可以说,一个功能稍微强大的网络应用程序总会在其中开出或多或少的线程,如果应用程序中开出的线程数目大于二个,那么就可以把这个程序称之为多线程应用程序。那么为什么在网络应用程序总会和线程交缠在一起呢?这是因为网络应用程序在执行的时候,会遇到很多意想不到的问题,其中最常见的是网络阻塞和网络等待等。程序在处理这些问题的时候往往需要花费很多的时间,如果不使用线程,则程序在执行时的就会表现出如运行速度慢,执行时间长,容易出现错误、反应迟钝等问题。而如果把这些可能造成大量占用程序执行时间的过程放在线程中处理,就往往能够大大提高应用程序的运行效率和性能和获得更优良的可伸缩性。那么这是否就意味着应该在网络应用程序中广泛的使用线程呢?情况并非如此,线程其实是一把双刃剑,如果不分场合,在不需要使用的地方强行使用就可能会产生许多程序垃圾,或者在程序结束后,由于没有能够销毁创建的进程而导致应用程序挂起等问题。所以如果你认为自己编写的代码足够快,那我给你的建议还是别使用线程或多线程。这里要提醒诸位的是如果您对在Windows下的线程和其执行原理和机制还不十分清楚,可以先参阅一下介绍Windows操作系统方面的书籍,它们一般都会对其进行比较详细的阐述。然后再阅读本文。一、简介在VisualC#中创建和使用线程:VisualC#中使用的线程都是通过自命名空间System.Threading中的Thread类经常实例化完成的。通过Thread类的构造函数来创建可供VisualC#使用的线程,通过Thread中的方法和属性来设定线程属性和控制线程的状态。以下Thread类中的最典型的构造函数语法,在VisualC#中一般使用这个构造函数来创建、初始化Thread实例。publicThread(ThreadStartstart);参数:startThreadStart委托,它将引用此线程开始执行时要调用的方法。Thread还提供了其他的构造函数来创建线程,这里就不一一介绍了。表01是Thread类中的一些常用的方法及其简要说明:方法说明Abort调用此方法通常会终止线程,但会引起ThreadAbortException类型异常。Interrupt中断处于WaitSleepJoin线程状态的线程。Join阻塞调用线程,直到某个线程终止时为止。ResetAbort取消当前线程调用的Abor方法。Resume继续已挂起的线程。Sleep当前线程阻塞指定的毫秒数。Start操作系统将当前实例的状态更改为ThreadState.Running。Suspend挂起线程,或者如果线程已挂起,则不起作用。表01:Thread类的常用方法及其说明这里要注意的是在.Net中执行一个线程,当线程执行完毕后,一般会自动销毁。如果线程没有自动销毁可通过Thread中的Abort方法来手动销毁,但同样要注意的是如果线程中使用的资源没有完全销毁,Abort方法执行后,也不能保证线程被销毁。在Thread类中还提供了一些属性用以设定和获取创建的Thread实例属性,表02中是Thread类的一些常用属性及其说明:属性说明CurrentCulture获取或设置当前线程的区域性。CurrentThread获取当前正在运行的线程。IsAlive获取一个值,该值指示当前线程的执行状态。IsBackground获取或设置一个值,该值指示某个线程是否为后台线程。Name获取或设置线程的名称。Priority获取或设置一个值,该值指示线程的调度优先级。ThreadState获取一个值,该值包含当前线程的状态。表02:Thread类的常用属性及其说明二、本文的主要内容及程序调试和运行环境:本文的主要内容是介绍多线程给用VisualC#编写网络应用程序带来的更高性能提高。具体的做法是在VisualC#用二种不同的方法,一种采用了多线程,另一种不是,来实现同一个具体网络应用示例,此示例的功能是获取网络同一网段多个IP地址对应的计算机的在线状态和对应的计算机名称,通过比较这二种方法的不同执行效率就可知多线程对提高网络应用程序的执行效率是多么的重要了。以下是本文中设计到程序的调试和运行的基本环境配置:(1)微软公司视窗2000服务器版。(2)VisualStudio.Net2002正式版,.NetFrameWorkSDK版本号3705。三、扫描网络计算机的原理:下面介绍的这个示例的功能是通过扫描一个给定区间IP地址,来判断这些IP地址对应的计算机是否在线,如果在线则获得IP地址对应的计算机名称。程序判断计算机是否在线的是采用对给定IP地址的计算机进行DNS解析,如果能够根据IP地址解析出对应的计算机名称,则说明此IP地址对应的计算机在线;反之,如果解析不出,则会产生异常出错,通过对异常的捕获,得到此IP地址对应的计算机并不在线。为了更清楚地说明问题和便于掌握在VisualC#编写多线程网络应用程序的方法,本文首先介绍的是不基于多线程的网络计算机扫描程序的编写步骤,然后再在其基础上,把它修改成多线程的计算机扫描程序,最后比较这二个程序的执行效率,你就会发现线程在网络编程中的重要作用了。四、VisualC#实现不基于多线程的网络计算机扫描程序以下是在VisualC#实现不基于多线程的网络计算机扫描程序步骤:1、启动VisualStudio.Net,并新建一个VisualC#项目,项目名称为【扫描网络计算机】。2、把VisualStudio.Net的当前窗口切换到【Form1.cs(设计)】窗口,并从【工具箱】中的【Windows窗体组件】选项卡中往Form1窗体中拖入下列组件,并执行相应操作:四个NumericUpDown组件,用以组合成一个IP地址区间。一个ListBox组件,用以显示扫描后的结果。一个ProgressBar组件,用以显示程序的运行进度。四个Label组件,用以显示提示信息。一个GroupBox组件。一个Button组件,名称为button1,并在这组件拖入窗体后,双击button1,这样VisualStudio.Net就会产生这button1组件Click事件对应的处理代码。界面设置如下图:图01:【扫描网络计算机】项目的设计界面3、把VisualStudio.Net的当前窗口切换到【Form1.cs】,进入Form1.cs文件的编辑界面。在Form1.cs头部,用下列代码替换系统缺省的导入命名空间代码:usingSystem;usingSystem.Drawing;usingSystem.Collections;usingSystem.ComponentModel;usingSystem.Windows.Forms;usingSystem.Data;usingSystem.Net.Sockets;usingSystem.Net;4、用下列代码替换Form1.cs中的button1的Click时间对应的处理代码,下列代码的功能是扫描给定的IP地址区间,并把扫描结果显示出来。privatevoidbutton1_Click(objectsender,System.EventArgse){listBox1.Items.Clear();//清楚扫描结果显示区域DateTimeStartTime=DateTime.Now;//获取当前时间stringmask=numericUpDown1.Value.ToString()+.+numericUpDown2.Value.ToString()+.+numericUpDown3.Value.ToString()+.;intMin=(int)numericUpDown4.Value;intMax=(int)numericUpDown5.Value;if(MinMax){MessageBox.Show(输入的IP地址区间不合法,请检查!,错误!);return;}//判断输入的IP地址区间是否合法progressBar1.Minimum=Min;progressBar1.Maximum=Max;inti;for(i=Min;i=Max;i++){stringip=mask+i.ToString();IPAddressmyIP=IPAddress.Parse(ip);//根据给定的IP地址字符串,处境IPAddress实例try{IPHostEntrymyHost=Dns.GetHostByAddress(myIP);stringHostName=myHost.HostName.ToString();listBox1.Items.Add(ip+名称为:+HostName);}catch{listBox1.Items.Add(ip+主机没有响应!);}progressBar1.Value=i;}//扫描给定IP地址对应的计算机是否在线DateTimeEndTime=DateTime.Now;TimeSpants=EndTime-StartTime;//获得扫描网络计算机所使用的时间label4.Text=ts.Seconds.ToString()+秒;MessageBox.Show(成功完成检测!,提示);progressBar1.Value=Min;}由于上述代码比较简单,并且在代码中的注释也比较详细,这里就不加以解释了,但请注意上面代码中对时间日期类型数据的处理方法。因为有很多人曾经向我讯问过类似问题。5、至此,不基于多线程的【扫描网络计算机】项目的全部工作就完成了,程序的执行是很机械的,其方法是对每一个IP按照顺序进行DNS解析,并得到解析结果,所以程序的执行时间和扫描的IP地址区间段大小成正比。图02是此程序运行后,扫描10.138.198.1至10.138.198.10这个IP地址区间计算机后的运行界面。整个程序的运行时间为43秒:图02:不基于多线程的【扫描网络计算机】项目的运行界面五、把【扫描网络计算机】程序修改成基于多线程的程序:在修改成多线程程序之前,必须面对并解决下面几个问题:1、线程是无返回值的,所以在线程中处理、调用的应是一个过程,所以要把扫描IP地址对应的计算机的代码给包装成一个过程。2、放在线程中处理的过程,因为没有返回值,从而无法向主程序(进程)传递数值。但扫描IP地址对应的计算机的过程却要向主程序(进程)传递IP地址是否在线的数据,所以在修改成多线程程序之前,必须从线程往主程序(进程)传递数据的问题。下面是在【扫描网络计算机】项目的基础上,把它修改成基于多线程程序的具体实现步骤:1、由于程序中使用到线程,所以在Form1.cs代码首部,导入命名空间代码区中加入下列代码,下列代码是导入Thread类所在的命名空间。usingSystem.Threading;2、在Form1.cs代码的namespace代码区加入下列语句,下列语句是定义一个delegate:publicdelegatevoidUpdateList(stringsIP,stringsHostName);3、在Form1.cs中的定义Form1的class代码区定义加入下列代码,下列代码是定义一个变量,用以存放程序执行的时间:privateSystem.DateTimeStartTime;4、在Form1.cs代码的Main函数之后,添加下列代码,下列代码是创建一个名称为ping的Class,这个Class能够通过其设定的属性接收给定的IP地址字符串,并据此来判断此IP地址字符串对应的计算机是否在线,并通过其设定的HostName属性接收从线程传递来的数据。publicclassping{publicUpdateListul;publicstringip;//定义一个变量,用以接收传送来的IP地址字符串publicstringHostName;//定义一个变量,用以向主进展传递对应IP地址是否在线数据publicvoidscan(){IPAddressmyIP=IPAddre