C#多线程编程实例空战单个载入程序/多个阅读程序在.Net类库中只不过早已获取了构建,即System.Threading.ReaderWriterLock类。本文通过对少见的单个载入/多个阅读程序的分析来探寻c#的多线程编程。问题的明确提出所谓单个载入程序/多个阅读程序的线程实时问题,是指给定数量的线程访问共享资源时,载入程序(线程)必须改动共享资源,而阅读程序(线程)必须读取数据。在这个实时问题中,很更容易获得下面二个拒绝:1)当一个线程正在载入数据时,其他线程无法写出,也无法读书。
2)当一个线程正在读取数据时,其他线程无法写出,但需要读书。1/21在数据库应用程序环境中常常遇上这样的问题。比如说,有n个最终用户,他们都要同时采访同一个数据库。
其中有m个用户要将数据现金数据库,n-m个用户要加载数据库中的记录。很似乎,在这个环境中,我们无法让两个或两个以上的用户同时改版同一条记录,如果两个或两个以上的用户都企图同时改动同一记录,那么该记录中的信息就不会被毁坏。
我们也不想一个用户改版数据库记录的同时,让另一用户加载记录的内容。因为加载的记录很有可能同时包括了改版和没改版的信息,也就是说这条记录是违宪的记录。2/21构建分析规定任一线程要对资源展开写出或读书操作前必需申请人锁住。
根据操作者的有所不同,分成读者锁住和载入锁住,操作者已完成之后不应获释适当的锁住。将单个载入程序/多个阅读程序的拒绝转变一下,可以获得如下的形式:一个线程申请人读者锁住的顺利条件是:当前没活动的载入线程。一个线程申请人载入锁住的顺利条件是:当前没任何活动(对锁住而言)的线程。
3/21因此,为了标志否有活动的线程,以及是载入还是读者线程,引进一个变量m_nActive,如果m_nActive0,则回应当前活动读者线程的数目,如果m_nActive=0,则回应没任何活动线程,m_nActive0,回应当前有载入线程在活动,留意m_nActive0,时不能所取-1的值,因为只容许有一个载入线程活动。为了辨别当前活动线程享有的锁住的类型,我们使用了线程局部存储技术(请参阅其它参照书籍),将线程与类似标志位关联一起。
4/21申请人读者锁住的函数原型为:publicvoidAcquireReaderLock(intmillisecondsTimeout),其中的参数为线程等候调度的时间。函数定义如下:publicvoidAcquireReaderLock(intmillisecondsTimeout){//m_mutext迅速可以获得,以便转入临界区m_mutex.WaitOne();//否有载入线程不存在boolbExistingWriter=(m_nActive0);if(bExistingWriter){//等候读者线程数目特1,当有锁获释时,根据此数目来调度线程m_nWaitingReaders++;}5/21else{//当前活动线程特1m_nActive++;}m_mutex.ReleaseMutex();//存储锁住标志为ReaderSystem.LocalDataStoreSlotslot=Thread.GetNamedDataSlot(m_strThreadSlotName);objectobj=Thread.GetData(slot);LockFlagsflag=LockFlags.None;if(obj!=null)flag=(LockFlags)obj;6/21if(flag==LockFlags.None){Thread.SetData(slot,LockFlags.Reader);}else{Thread.SetData(slot,(LockFlags)((int)flag|(int)LockFlags.Reader));}if(bExistingWriter){//等候登录的时间this.m_aeReaders.WaitOne(millisecondsTimeout,true);}}它首先转入临界区(借以在多线程环境下确保活动线程数目的操作者的正确性)辨别当前活动线程的数目,如果有写出线程(m_nActive0)不存在,则等候登录的时间并且等候的读者线程数目特1。如果当前活动线程是读书线程(m_nActive=0),则可以让读书线程之后运营。7/21申请人载入锁住的函数原型为:publicvoidAcquireWriterLock(intmillisecondsTimeout),其中的参数为等候调度的时间。
函数定义如下:publicvoidAcquireWriterLock(intmillisecondsTimeout){//m_mutext迅速可以获得,以便转入临界区m_mutex.WaitOne();//否有活动线程不存在boolbNoActive=m_nActive==0;if(!bNoActive){m_nWaitingWriters++;}else{m_nActive--;}8/21m_mutex.ReleaseMutex();//存储线程锁住标志System.LocalDataStoreSlotslot=Thread.GetNamedDataSlot(myReaderWriterLockDataSlot);objectobj=Thread.GetData(slot);LockFlagsflag=LockFlags.None;if(obj!=null)flag=(LockFlags)Thread.GetData(slot);if(flag==LockFlags.None){Thread.SetData(slot,LockFlags.Writer);}else{Thread.SetData(slot,(LockFlags)((int)flag|(int)LockFlags.Writer));}9/21//如果有活动线程,等候登录的时间if(!bNoActive)this.m_aeWriters.WaitOne(millisecondsTimeout,true);}它首先转入临界区辨别当前活动线程的数目,如果当前有活动线程不存在,不管是写出线程还是读书线程(m_nActive),线程将等候登录的时间并且等候的载入线程数目特1,否则线程享有写出的权限。10/21获释读者锁住的函数原型为:publicvoidReleaseReaderLock()。函数定义如下:publicvoidReleaseReaderLock(){System.LocalDataStoreSlotslot=Thread.GetNamedDataSlot(m_strThreadSlotName);LockFlagsflag=(LockFlags)Thread.GetData(slot);if(flag==LockFlags.None){return;}boolbReader=true;11/21switch(flag){caseLockFlags.None:break;caseLockFlags.Writer:bReader=false;break;}if(!bReader)return;Thread.SetData(slot,LockFlags.None);m_mutex.WaitOne();AutoResetEventautoresetevent=null;this.m_nActive--;12/21if(this.m_nActive==0){if(this.m_nWaitingReaders0){m_nActive++;m_nWaitingReaders--;autoresetevent=this.m_aeReaders;}13/21elseif(this.m_nWaitingWriters0){m_nWaitingWriters--;m_nActive--;autoresetevent=this.m_aeWriters;}}m_mutex.ReleaseMutex();if(autoresetevent!=null)autoresetevent.Set();}14/21获释读者锁住时,首先辨别当前线程否享有读者锁住(通过线程局部存储的标志),然后辨别否有等候的读者线程,如果有,再行将当前活动线程特1,等候读者线程数目减半1,然后改置事件为有信号。
如果没等候的读者线程,辨别否有等候的载入线程,如果有则活动线程数目减半1,等候的载入线程数目减半1。获释载入锁住与获释读者锁住的过程大致相同,可以参见源代码。留意在程序中,获释锁住时,只不会苏醒一个阅读程序,这是因为用于AutoResetEvent的原历,读者可自行将其改为ManualResetEvent,同时苏醒多个阅读程序,此时应令m_nActive相等整个等候的读者线程数目。
15/21申请人载入锁住的函数原型为:publicvoidAcquireWriterLock(intmillisecondsTimeout),其中的参数为等候调度的时间。函数定义如下:publicvoidAcquireWriterLock(intmillisecondsTimeout){//m_mutext迅速可以获得,以便转入临界区m_mutex.WaitOne();//否有活动线程不存在boolbNoActive=m_nActive==0;if(!bNoActive){m_nWaitingWriters++;}else{m_nActive--;}16/21m_mutex.ReleaseMutex();//存储线程锁住标志System.LocalDataStoreSlotslot=Thread.GetNamedDataSlot(myReaderWriterLockDataSlot);objectobj=Thread.GetData(slot);LockFlagsflag=LockFlags.None;if(obj!=null)flag=(LockFlags)Thread.GetData(slot);if(flag==LockFlags.None){Thread.SetData(slot,LockFlags.Writer);}else{Thread.SetData(slot,(LockFlags)((int)flag|(int)LockFlags.Writer));}17/21//如果有活动线程,等候登录的时间if(!bNoActive)this.m_aeWriters.WaitOne(millisecondsTimeout,true);}它首先转入临界区辨别当前活动线程的数目,如果当前有活动线程不存在,不管是写出线程还是读书线程(m_nActive),线程将等候登录的时间并且等候的载入线程数目特1,否则线程享有写出的权限。
18/21获释读者锁住的函数原型为:publicvoidReleaseReaderLock()。函数定义如下:publicvoidReleaseReaderLock(){System.LocalDataStoreSlotslot=Thread.GetNamedDataSlot(m_strThreadSlotName);LockFlagsflag=(LockFlags)Thread.GetData(slot);if(flag==LockFlags.None){return;}boolbReader=true;19/21switch(flag){caseLockFlags.None:break;caseLockFlags.Writer:bReader=false;break;}if(!bReader)return;Thread.SetData(slot,LockFlags.None);m_mutex.WaitOne();AutoResetEventautoresetevent=null;this.m_nActive--;20/21if(this.m_nActive==0){if(this.m_nWaitingReaders0){m_nActive++;m_nWaitingReaders--;autoresetevent=this.m_aeReaders;}elseif(this.m_nWaitingWriters0){m_nWaitingWriters--;m_nActive--;autoresetevent=this.m_aeWriters;}}21/21m_mutex.ReleaseMutex();if(autoresetevent!=null)autoresetevent.Set();}获释读者锁住时,首先辨别当前线程否享有读者锁住(通过线程局部存储的标志),然后辨别否有等候的读者线程,如果有,再行将当前活动线程特1,等候读者线程数目减半1,然后改置事件为有信号。如果没等候的读者线程,辨别否有等候的载入线程,如果有则活动线程数目减半1,等候的载入线程数目减半1。
获释载入锁住与获释读者锁住的过程大致相同,可以参见源代码。留意在程序中,获释锁住时,只不会苏醒一个阅读程序,这是因为用于AutoResetEvent的原历,读者可自行将其改为ManualResetEvent,同时苏醒多个阅读程序,此时应令m_nActive相等整个等候的读者线程数目。
本文来源:米乐|米乐·M6-www.sdxunde.com
地址:重庆市重庆市重庆区明标大楼75号 电话:0898-08980898 手机:15482233098
Copyright © 2006-2024 www.sdxunde.com. 米乐|米乐·M6科技 版权所有 ICP备案编号:ICP备74254883号-5