多线程编程进程(process)和线程(thread)当一个程序开始运行时,它就是一个进程。进程包括运行中的程序和程序使用的内存和系统资源。而一个进程又是由多个线程组成的,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。多线程(Multi-thread)的概念旅游多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说,允许单个程序创建多个并行执行的线程来完成各自的任务。浏览器就是一个很好的多线程例子,在浏览器中可以在下载Java小应用程序或图像的同时滚动页面,在访问新页面时播放动画、声音并打印文件等。线程的namespaceSystem.Threading其中Thread类用于创建线程;ThreadPool类用于管理线程池程序1:显示线程状态ShowThreadThread.CurrentThread得到当前的线程Thread类有几个至关重要的方法,描述如下:(1)Start()——启动线程。(2)Sleep(int)——静态方法,暂停当前线程指定的毫秒数。(3)Abort()——通常使用该方法来终止一个线程。(4)Suspend()——该方法并不终止未完成的线程,而只是挂起线程,以后还可恢复。(5)Resume()——恢复被Suspend()方法挂起的线程的执行。线程的创建使用Thread类创建线程时,只需提供线程入口。线程入口告诉程序让这个线程做什么,在C#中,线程入口是通过ThreadStart代理(delegate)提供的可以把ThreadStart理解为一个函数指针,指向线程要执行的函数,当调用Thread.Start()方法后,线程就开始执行ThreadStart所代表(或说指向)的函数。程序2:创建线程CreateThread这段程序包含两个类Alpha和Program,在创建线程oThread时我们用指向Alpha.Beta()方法初始化了ThreadStart代理(delegate)对象,当线程oThread调用oThread.Start()方法启动时,实际上程序运行的是Alpha.Beta()方法:AlphaoAlpha=newAlpha();ThreadoThread=newThread(newThreadStart(oAlpha.Beta));oThread.Start();然后在Main()函数的while循环中,我们使用静态方法Thread.Sleep()让主线程暂停了1ms,这段时间CPU转向执行线程oThread。然后我们试图用Thread.Abort()方法终止线程oThread,注意后面的oThread.Join(),Thread.Join()方法将使主线程等待,直到oThread线程结束为止。可以给Thread.Join()方法指定一个int型的参数作为等待的最长时间。之后,我们试图用Thread.Start()方法重新启动线程oThread,但是使用Abort()方法的后果是不可恢复地终止线程,所以最后程序会抛出ThreadStateException异常注意:其他线程都依附于Main()函数所在的线程,Main()函数是C#程序的入口,起始线程可以称为主线程,如果所有的前台线程都停止了,那么主线程可以终止,而所有的后台线程都将无条件终止。在微观上,所有的线程是串行执行的,但在宏观上,可以认为它们在并行执行。Thread.ThreadState这个属性,它代表了线程运行时的状态,在不同的情况下有不同的值.ThreadState在各种情况下的可能取值如下:(1)Aborted——线程已停止;(2)AbortRequested——线程的Thread.Abort()方法已被调用,但是线程还未停止;(3)Background——线程在后台执行,与属性Thread.IsBackground有关;(4)Running——线程正常运行;(5)Stopped——线程已被停止;(6)StopRequested——线程正在被要求停止;(7)Suspended——线程已被挂起(此状态下,可以通过调用Resume()方法重新运行);(8)SuspendRequested——线程正在要求被挂起,但未来得及响应;(9)Unstarted——未调用Thread.Start()开始线程的运行;(10)WaitSleepJoin——线程因为调用了Wait()、Sleep()或Join()等方法而处于封锁状态线程的优先级当线程之间争夺CPU时间时,CPU按照线程的优先级给予服务。在C#应用程序中,用户可以设定5个不同的优先级,由高到低分别是Highest、AboveNormal、Normal、BelowNormal、Lowest,在创建线程时如果不指定优先级,系统将默认为ThreadPriority.Normal。给一个线程指定优先级,可以使用如下代码://设定优先级为最低myThread.Priority=ThreadPriority.Lowest;通过设定线程的优先级,可以安排一些相对重要的线程优先执行,如对用户的响应等。用多线程编制局域网聊天程序3:iChat另起一个线程监测接收端口!线程安全的控件访问线程的同步和通信假设有这样一种情况,两个线程同时维护一个队列,如果一个线程向队列添加元素,而另一个线程从队列中取用元素,那么称添加元素的线程为生产者,取用元素的线程为消费者。生产者与消费者问题看起来很简单,却是多线程应用中一个必须解决的问题,它涉及线程之间的同步和通信问题。Lock每个线程都有自己的资源,但代码区是共享的,即每个线程都可以执行相同的函数。但在多线程环境下,可能带来这样的问题:几个线程同时执行一个函数,导致数据混乱,产生不可预料的结果,我们必须避免这种情况发生。C#提供了一个关键字lock,它可以把一段代码定义为互斥段(criticalsection)。互斥段在一个时刻只允许一个线程进入执行,而其他线程必须等待。在C#中,关键字lock定义如下:lock(expression)statement_blockexpression代表希望跟踪的对象,通常是对象引用。一般地,保护一个类的实例,可以使用this;而保护一个静态变量(如互斥代码段在一个静态方法内部),使用类名就可以了。statement_block就是互斥段的代码,这段代码在一个时刻只能被一个线程执行。程序4:Ex9-1Lock防止代码重入与数据库的锁原理一样Join等待Join()使主线程等待,直至子线程完成Suspend挂起线程Resume恢复线程运行!Resume必须和Suspend成对!停止(运行完成或被终止)的线程不可以再start,须重新new生成实例线程池和定时器在多线程的程序中,经常会出现两种情况。第一种情况是,应用程序中的线程把大部分时间花费在等待状态,等待某个事件发生,然后才能给予响应;第二种情况则是,线程平常都处于休眠状态,只是周期性地被唤醒。,在.netframework里面,我们使用ThreadPool来应对第一种情况,使用Timer来应对第二种情况。线程池ThreadPool类提供一个由系统维护的线程池——可以看作一个线程的容器,因为其中某些方法调用了只有高版本Windows才有的API函数,所以该容器需要Windows2000以上版本的系统支持。可以使用ThreadPool.QueueUserWorkItem()方法将线程安放在线程池里,该方法的原型如下:publicstaticboolQueueUserWorkItem(WaitCallback);重载的方法如下,参数object将传递给WaitCallback所代表的方法。注意:ThreadPool类也是一个静态类,用户不能也不必生成它的对象,而且一旦使用该方法在线程池中添加了一个项目,那么该项目无法取消。这里用户无须自己建立线程,只需把要做的工作写成函数,作为参数传递给ThreadPool.QueueUserWorkItem()方法,传递的方法就是依靠WaitCallback代理对象,而线程的建立、管理、运行等工作都是由系统自动完成的,用户无须考虑复杂的细节问题,线程池的优点也就体现在这里,就像公司老板——只需安排工作,不必亲自动手。注意:ThreadPool类也是一个静态类,用户不能也不必生成它的对象,而且一旦使用该方法在线程池中添加了一个项目,那么该项目无法取消。这里用户无须自己建立线程,只需把要做的工作写成函数,作为参数传递给ThreadPool.QueueUserWorkItem()方法,传递的方法就是依靠WaitCallback代理对象,而线程的建立、管理、运行等工作都是由系统自动完成的定时器与ThreadPool类不同,Timer类的作用是设置一个定时器,定时执行用户指定的函数,而这个函数的传递是靠另外一个代理对象TimerCallback,它必须在创建Timer对象时就指定,并且不能更改。定时器启动后,系统将自动建立一个新线程,并在这个线程里执行用户指定的函数。下面的语句初始化一个Timer对象:Timertimer=newTimer(timerDelegate,s,1000,1000);第1个参数指定TimerCallback代理对象;第2个参数的意义跟上面提到的WaitCallback代理对象一样,作为一个对象传递给要调用的方法,表示状态;第3个参数是延迟时间——计时开始的时刻距现在的时间,单位是毫秒;第4个参数是定时器的时间间隔——计时开始以后,每隔相同的一段时间,TimerCallback所代表的方法被调用一次,单位也是毫秒。上面语句的意思就是将定时器的延迟时间和时间间隔都设为1秒钟。定时器的设置是可以改变的,调用Timer.Change()方法即可,这是一个参数类型重载的方法,一般使用的原型如下:publicboolChange(long,long);下面这行代码将前边设置的定时器修改了一下:timer.Change(10000,2000);显然,定时器timer的时间间隔被重新设置为2秒,停止计时10秒后生效。互斥对象多线程代码和资源的同步问题、自动化管理及定时触发的问题已经得到解决,那么如何控制多个线程之间的联系呢?例如,顾客到餐厅吃饭,在吃饭之前先得等厨师把饭菜做好,然后开始吃,吃完还得付款,付款方式可以是现金或信用卡,付款之后才能离开。在这个过程里,“吃饭”可以看作是主线程,“厨师做饭”是一个线程,服务员用“信用卡收款”和“收现金”可以看作两个线程,其中的关系很清楚——“吃饭”必须等待“厨师做饭”,然后等待两个“收款”线程中的任意一个完成,然后“吃饭”线程执行“离开”步骤,于是“吃饭”才算结束了。事实上,现实中有比这更复杂的联系,怎样才能很好地控制它们而不产生冲突和重复呢?这种情况下,我们要用到互斥对象,即System.Threading命名空间中的Mutex类。我们可以把Mutex看作一个出租车,乘客就是线程,他首先等车,然后上车,最后下车。当一个乘客在车上时,其他乘客只有等他下车以后才可以上车。线程与Mutex对象的关系也是如此,线程使用Mutex.WaitOne()方法等待Mutex对象被释放,一旦Mutex对象被释放,它就自动拥有这个对象,直到它调用Mutex.ReleaseMutex()方法释放这个对象,而在此期间,其他想要获取这个Mutex对象的线程只有等待。示例:MutexDemo互斥保证同一对象在同一时间只被一个线程占有SingleForm