浅谈.net多线程机制
最近由于工作需要,对多线程进行了深入研究,也只能略知一二,尚不能融会贯通。如有不妥之处,还请大牛们不吝赐教。
本文不会详细列出各种多线程的例子和源代码。本文旨在给那些和我当初对多线程理不清头绪的人投石问路。
一、多线程原理:
Windows是一个多任务的系统,当一个程序开始运行时,它就是一个进程,进程所指包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的,线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
二、多线程分类:
1. Critical section:临界区
2. Mutex:互斥
3.Event:事件
4.线程池
园子里很多关于多线程的文章都只是片面的只介绍其中一种实现方法,并没有给出全貌,往往让我们感觉摸不着头脑。
其实在.NET中,也就只有以上四种机制,这四种机制,分别都有实现,有些还不止一种实现。
1.首先说一下“Critical section”: 临界区顾名思义就是一块不能并发访问的区域,同一时刻只能有一个线程占有它。
它的实现有:
a. Lock:
有可能多个线程共用同一个函数.想象一下:如果同一个函数被多个线程同事执行会是什么后果?但是如果你使用了Lock机制,就可以避免。
但是Lock也不能乱用,因为容易发生死锁。
b.Monitor:
共用对象互斥,多个线程共用同一个对象。原理和Lock有异曲同工之妙,有时他们的应用场景是一样的。
Queue oQueue=new
Queue();
Monitor.Enter(oQueue);//现在oQueue对象只能被当前线程操纵了
Monitor.Exit(oQueue);//释放锁
为了保证线程最终都能释放锁,你可以把Monitor.Exit()方法写在try-catch-finally结构中的finally代码块里。
当拥有对象锁的线程准备释放锁时,它使用Monitor.Pulse()方法通知等待队列中的第一个线程,于是该线程被转移到预备队列中,当对象锁被释放时,在预备队列中的线程可以立即获得对象锁。
多谢浪雪,让我明白了Lock其实就是Monitor的便捷语法。以前只知道他们功能极其相似,惭愧。。
2.Mutex
一个同步基元,也可用于进程间同步。你可以把它想象成一个令牌,只有获得该令牌的线程才有权访问资源,否则只能等待。
当两个或更多线程需要同时访问一个共享资源时,系统需要使用同步机制来确保一次只有一个线程使用该资源。
Mutex 是同步基元,它只向一个线程授予对共享资源的独占访问权。
如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。
Thread.Suspend,Thread.Resume已经过时了,这个大家应该都知道吧?其实使用Mutex很容易就实现了。
Thread.Suspend:就是收回令牌,令所有的线程都等待(暂停),Thread.Resume就是将令牌归还。
个人觉得介绍Mutex比较好的的文章: http://www.cnblogs.com/city22/archive/2007/02/02/638260.html#2349012
以上是摘抄:
Mutex是一个令牌,当一个线程拿到这个令牌时运行,另外想拿到令牌的线程就必须等待,直到拿到令牌的线程释放令牌。没有所有权的线程是无法释放令牌的。
Mutex(false,”string”)中的string是令牌的关键,或者可以叫令牌名,因为Mutex是跨进程的,整个系统中只会有唯一的令牌存在所以,也就是说你在一个应用程序中的一个线程中得到了Mutex的所有权,那在另外一个线程中的另外的线程想得到他就必须要等待。
要弄清楚Mutex就还需要弄清楚两个很重要的问题:
1.那就是Mutex是调用的Win32 的API
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
);
这就是他为什么能跨进程访问的原因,正是由于它使用P/Invoke,他的效率问题就凸现出来,明显不如Monitor之类的快,用的时候还需多多斟酌。
3.Event
按照我的理解,它和Mutext区别在于:实现方式不一样。Mutext是靠一个基元,而Event是靠事件通知。
它包含:
AutoRestEvent: http://msdn.microsoft.com/zh-cn/library/system.threading.autoresetevent.aspx
ManualResetEvent: http://msdn.microsoft.com/zh-cn/library/system.threading.manualresetevent(v=VS.80).aspx
AutoResetEvent与ManualResetEvent的区别大家可以自己去找,因为自己不是很了解,不敢乱说。
但是AutoResetEvent的效率要高些,而ManualResetEvent更灵活一些。
4.线程池(ThreadPool)
提供一个线程池,该线程池可用于执行任务、发送工作项、处理异步 I/O、代表其他线程等待以及处理计时器。
许多应用程序创建的线程都要在休眠状态中消耗大量时间,以等待事件发生。 其他线程可能进入休眠状态,只被定期唤醒以轮询更改或更新状态信息。 线程池通过为应用程序提供一个由系统管理的辅助线程池,使您可以更为有效地使用线程。
http://msdn.microsoft.com/zh-cn/library/system.threading.threadpool.aspx
说到ThreadPool不得不说Semaphore(信号灯,旗语)
它是最高效的多线程控制方法,因为它是限制可同时访问某一资源或资源池的线程数。也就是说,你可以让多个线程“同时访问”一个临界资源。虽然这样讲不对,但是你可以这么理解。
5.拿不准:
关于线程池还有一个不得不提:System.Threading.Timer ,它是是一个简单的轻量计时器,它使用回调方法并由线程池线程提供服务。
http://msdn.microsoft.com/zh-cn/library/system.threading.timer.aspx
还有其它的,请补充。