強化VB.NET編程多線程句柄技巧
VB.NET編程經過長時間的發(fā)展,很多用戶都很了解VB.NET編程中多線程程序。多線程成為大多程序員苦惱的事,現(xiàn)在和大家交流一下多線程。多線程是可行的,因為操作系統(tǒng)是多任務的,它有模擬同一時刻運行多個應用程序的能力。盡管多數(shù)個人計算機只有一個處理器,但是現(xiàn)在的操作系統(tǒng)還是通過在多個執(zhí)行代碼片斷之間劃分處理器時間提供了多任務。線程可能是整個應用程序,但通常是應用程序可以單獨運行的一個部分。操作系統(tǒng)根據(jù)線程的優(yōu)先級和離最近運行的時間長短給每一個線程分配處理時間。多線程對于時間密集型事務(例如文件輸入輸出)應用程序的性能有很大的提高。VB.NET編程中通常使用三類等待句柄:互斥對象、ManualResetEvent和AutoResetEvent。后兩種通常用于同步事件。
1.互斥對象
互斥對象都是同步對象,它們只能在一個時刻由一個線程擁有。實際上,互斥這個名字衍生自互斥對象的所有權是相互排斥的。當線程請求獨占訪問某種資源時,它們請求互斥對象的所有權。因為在某個時刻只有一個線程能擁有一個互斥對象,其它線程在使用資源前必須等待互斥對象的所有權。WaitOne方法引發(fā)一個調用線程等待互斥對象的所有權。如果擁有互斥對象的線程正常終止,該互斥對象的狀態(tài)就被設置為signaled,下一個線程獲得它的所有權。
2.同步事件
同步事件用于通知其它的線程發(fā)生了某種事情或者某種資源可用。不要被它使用了"事件"這個詞迷惑了。同步事件與其它的VisualBasic事件不同,它是真正的等待句柄。與其它的等待句柄類似,同步事件有兩種狀態(tài)signaled 和nonsignaled。調用同步事件的某個等待方法的線程必須等待,直到其它線程調用Set方法給事件發(fā)信號。有兩個同步事件類。線程使用Set方法把ManualResetEvent實例的狀態(tài)設置為signaled。線程使用Reset方法或控制返回等待WaitOne調用把實例的狀態(tài)設置為nonsignaled。AutoResetEvent類的實例也可以使用Set設置為signaled,但是只要通知等待線程事件變?yōu)閟ignaled,它們自動返回到nonsignaled。
下面的例子使用AutoResetEvent類同步線程池事務。
- SubStartTest()
- DimATAsNewAsyncTest()
- AT.StartTask()
- EndSub
- ClassAsyncTest
- PrivateSharedAsyncOpDoneAsNewSystem.Threading.AutoResetEvent(False)
- SubStartTask()DimTpoolAsSystem.Threading.ThreadPoolDimargAsString="SomeArg"
- Tpool.QueueUserWorkItem(NewSystem.Threading.WaitCallback(_AddressOfTask),arg)'對一個事務進行排隊
- AsyncOpDone.WaitOne()'等待該線程調用SetMsgBox("Threadisdone.")
- EndSubSubTask(ByValArgAsObject)
- MsgBox("Threadisstarting.")
- System.Threading.Thread.Sleep(4000)'等待4秒.
- MsgBox("Thestateobjectcontainsthestring"&CStr(Arg))
- AsyncOpDone.Set()'發(fā)信號表明該線程完成了
- EndSub
- EndClass
3.監(jiān)視對象和同步鎖
監(jiān)視對象確保代碼塊的運行不被運行在其它線程中的代碼打斷。換句話說,其它線程中的代碼不能運行,直到被同步的代碼塊結束。在VisualBasic .NET中使用SyncLock關鍵字來簡化監(jiān)視對象的訪問。在VisualC# .NET中使用Lock關鍵字。
例如,假定你有一個程序,它重復地、異步讀取數(shù)據(jù)并顯示結果。使用優(yōu)先多任務操作系統(tǒng),正在運行的線程可以因為操作系統(tǒng)允許其它的線程運行而被打斷。如果沒有同步,數(shù)據(jù)正在顯示時,顯示數(shù)據(jù)的對象被其它的線程修改,有可能得到的是部分更新的數(shù)據(jù)視圖。SyncLock保證一段代碼持續(xù)運行,不被打斷。下面的例子顯示了怎樣使用SyncLock給顯示過程提供數(shù)據(jù)對象的獨占訪問。
- ClassDataObject
- PublicObjTextAsString
- PublicObjTimeStampAsDate
- EndClassSubRunTasks()
- DimMyDataObjectAsNewDataObject()
- ReadDataAsync(MyDataObject)
- SyncLockMyDataObject
- DisplayResults(MyDataObject)
- EndSyncLock
- EndSub
- SubReadDataAsync(ByRefMyDataObjectAsDataObject)'添加異步讀取和處理數(shù)據(jù)的代碼
- EndSubSubDisplayResults(ByValMyDataObjectAsDataObject)'添加顯示結果的代碼
- EndSub
Interlocked類
你可以使用Interlocked類的方法防止多個線程同時更新或比較同一個值的問題發(fā)生。這個類的方法讓你安全地增加、減少、交換和比較來自任何線程的值。下面的例子演示了怎樣使用Increment方法增加一個運行在獨立線程上的多個過程共享的變量的值。
- SubThreadA(ByRefIntAAsInteger)
- System.Threading.Interlocked.Increment(IntA)
- EndSub
- SubThreadB(ByRefIntAAsInteger)
- System.Threading.Interlocked.Increment(IntA)
- EndSub
ReaderWriter鎖
在有些情況下,你可能希望只在寫數(shù)據(jù)時鎖定資源,在數(shù)據(jù)沒有更新完前允許多個客戶同時讀數(shù)據(jù)。某個線程正在修改資源時,ReaderWriterLock類加強了對該資源的獨占訪問,但是允許讀取資源的非獨占訪問。ReaderWriter鎖是排他鎖的一個有用的備選方案,排他鎖引起其它線程等待,即使這些線程不需要更新數(shù)據(jù)。下面的例子演示了怎樣使用ReaderWriter調整來自多個線程的讀和寫操作。
- ClassReadWrite
- 'ReadData和WriteData方法可以被多個線程安全地調用
- PublicReadWriteLockAsNewSystem.Threading.ReaderWriterLock()
- SubReadData()'這個過程從數(shù)據(jù)源讀取信息。在允許其它線程調用ReadData時,讀取鎖放置任何數(shù)據(jù)寫入直到讀取完成
- ReadWriteLock.AcquireReaderLock(System.Threading.Timeout.Infinite)Try'此處執(zhí)行數(shù)據(jù)操作
- FinallyReadWriteLock.ReleaseReaderLock()'釋放讀取鎖
- EndTry
- EndSubSubWriteData()'這個過程向數(shù)據(jù)源寫信息。寫入鎖防止數(shù)據(jù)被讀取或者寫入知道線程完成寫操作。
- ReadWriteLock.AcquireWriterLock(System.Threading.Timeout.Infinite)Try'此處執(zhí)行寫操作
- FinallyReadWriteLock.ReleaseWriterLock()'釋放寫入鎖
- EndTry
- EndSub
- EndClass
結論
多線程處理是可伸縮的、容易響應的應用程序的關鍵。VB.NET編程支持加強的、多線程開發(fā)模型,它使開發(fā)者迅速擁有了開發(fā)多線程應用程序的能力。
◆VisualBasic .NET使用新的.NET框架組件類,它使建立多線程應用程序更容易。
◆記住盡管多線程能提高性能,但是每個線程有建立線程需要的附加內存和保持它運行需要的處理器時間的花消。
◆線程的屬性和方法控制著線程間的交互操作,并且決定什么時候資源可以給運行的線程使用。
◆盡管多線程看起來帶來了混亂,但是你可以使用同步技術控制正在運行的線程。
◆盡管多線程增加了應用程序的復雜性,但是它通過高效率分配可用資源提高了應用程序的可伸縮性。
使用本文討論的技術,你可以開發(fā)和處理處理器密集型事務的專業(yè)應用程序。
【編輯推薦】