[C#]线程处理
线程处理用于使程序能够执行并发处理,同时执行多个操作。C#中有三种线程的使用方法,BackgroundWorker组件、线程池、自己创建使用线程,接下来分别介绍如何使用。
1.使用BackgroundWorker组件(创建多线程处理程序最可靠方法)
此类管理一个专用于处理指定方法的单独线程。
添加 DoWork 事件的事件处理程序,为后台操作做好准备,在此事件处理程序中调用耗时的操作。
调用 RunWorkerAsync 函数,开始后台操作。
处理 ProgressChanged 事件,收到进度更新的通知。
处理RunWorkerCompleted 事件,在操作完成时收到通知。
处理 ProgressChanged 和 RunWorkerCompleted 事件的方法可以访问应用程序的用户界面,原因是这两个事件是在调用了 RunWorkerAsync 方法的线程上引发的。 但是,DoWork 事件处理程序无法使用任何用户界面对象,因为它在后台线程上运行。
2.线程池,是可以用来在后台执行多个任务的线程集合,将任务添加到队列,然后在创建线程后自动启动这些任务。线程池通常用于服务器应用程序。 每个传入请求都将分配给线程池中的一个线程,因此可以异步处理请求,而不会占用主线程,也不会延迟后续请求的处理。一旦池中的某个线程完成任务,它将返回到等待线程队列中,等待被再次使用,这种重用使应用程序可以避免为每个任务创建新线程的开销。线程池通常具有最大线程数限制。如果所有线程都繁忙,则额外的任务将放入队列中,直到有线程可用时才能够得到处理。
ThreadPool.QueueUserWorkItem(WaitCallback callBack)//将方法排入队列以便执行。 ThreadPool.QueueUserWorkItem(WaitCallback callBack, object state)//将方法排入队列以便执行,并指定包含该方法所用数据的对象。
3.创建和使用线程
如果需要对应用程序的线程的行为进行更多的控制,则可以自己管理线程。通过声明类型为 Thread 的变量,并调用为要对新线程执行的过程或方法提供名称的构造函数,从而创建一个新线程。
System.Threading.Thread newThread = new System.Threading.Thread(AMethod);
线程其他知识点:
1.前台/后台线程
后台线程与前台线程完全一样,只是后台线程不会阻止进程终止。前台线程的运行时间不限定,而后台线程则在最后一个前台线程停止时立即停止。 可以使用 IsBackground 属性确定或更改线程的后台状态。默认情况下,主线程、通过Thread类构造函数创建的线程在前台执行,线程池线程、从非托管代码进入托管环境的线程在后台执行。
2.线程的参数和返回值
为线程提供参数:将目标方法包裹在类中,并为该类定义字段,这些字段将被用作新线程的参数。
从线程获取返回值:使用BackgroundWorker 组件来管理线程,在任务完成时引发RunWorkerCompleted 事件,然后用事件处理程序处理结果。
3.线程同步
在应用程序中使用多个线程,必须协调对资源(如文件句柄、网络连接和内存)的访问。C#中的线程同步方法有锁、监视器、同步事件和等待句柄、Mutex对象、Interlocked类、ReaderWriter锁。
锁:lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。
监视器:与lock关键字类似,防止多个线程同时执行代码块,但是lock更简洁、更安全。
同步事件和等待句柄:同步事件有终止、非终止两个状态的对象,用来激活和挂起线程,让线程等待一个非终止同步事件可以将线程挂起,将事件状态变为终止可以激活线程。如果线程尝试等待已经终止的事件,则线程继续执行,不会受到延迟。同步事件有AutoResetEvent和ManualResetEvent两种,只要AutoResetEvent 激活线程,其状态自动从终止变为非终止,ManualResetEvent 允许它的终止状态激活任意个线程,并只有调用其Reset方法时才还原到非终止状态。
通过调用WaitOne、WaitAny、WaitAll等待方法使线程等待事件,当调用事件的Set方法,事件变为终止状态。
using System; using System.Threading; class ThreadingExample { static AutoResetEvent autoEvent; static void DoWork() { Console.WriteLine(" worker thread started, now waiting on event..."); autoEvent.WaitOne(); Console.WriteLine(" worker thread reactivated, now exiting..."); } static void Main() { autoEvent = new AutoResetEvent(false); Console.WriteLine("main thread starting worker thread..."); Thread t = new Thread(DoWork); t.Start(); Console.WriteLine("main thread sleeping for 1 second..."); Thread.Sleep(1000); Console.WriteLine("main thread signaling worker thread..."); autoEvent.Set();
Console.ReadLine(); } }
执行结果
Mutex 对象,与监视器类似。
Interlock类,使用 Interlocked 类的方法来避免在多个线程尝试同时更新或比较同一个值时可能出现的问题。使用这个类的方法可以安全地递增、递减、交换和比较任何线程中的值。
ReadWriter锁,只在写入数据时锁定资源,在不更新数据时允许多个客户端同时读取数据。 ReaderWriterLock 类在线程修改资源时将强制其独占访问资源,但在读取资源时则允许非独占访问。ReaderWriterLock 类定义支持单个写线程和多个读线程的锁。
死锁,多线程应用程序始终存在deadlock 的危险,在编码前多考虑,避免死锁。
4.线程计时器。System.Threading.Timer 类可用于定期在单独的线程上运行任务,创建实例后计时器开始启动。