注意:此文只是講線程間的同步,其它同步不涉及。
線程同步是個好話題,因為寫線程經常會遇到,所以就寫寫自己知道的東西。
D裡面,同步(特指線程同步)從線程的角度來分,有幾種情況:
1:主線程與工作線程的同步
2:工作線程與主線程的同步
3:工作線程之間的同步。
同步,嗯,直白點講,或可以說成是:A線程怎麼通知B線程去做某某事,或者說某事需要:如何控制某一時間段內,A能做,B不能做(互斥)。
所以,手段很重要,也就有了API,也就有了方法:
1:lock(CriticalSection)
2:Event/Mutex/Semaphore + WaitFor
3:PostMessage/SendMessage/PostThreadMessage
簡單的做一下介紹,可能不詳細,具體請查看MSDN
一:鎖:臨界鎖:CriticalSection
通常我們所說的鎖,一般情況下都指這個臨界鎖CS。
在N線程間,對某數據,某資源的排它性, 互斥性的訪問,類似如下:
lock.enter();
inc(value);
lock.leave();
CS相對於上述其它類型的同步,是最快的同步操作了。
不過,鎖有N種,什麼SWRLock, spinlock。。。某些場合用某種鎖也是有分別的,需要自行查資料學習(學習不深,就不誤人子弟了)
二: Event/Mutex/Semaphore + WaitFor
這類同步,是信號燈類似的同步
1:Event: 事件信號
API:CreateEvent, SetEvent, ResetEvent + WaitFor
創建一event,然後通過waitfor函數檢查是否有信號,有則進入並做處理,無則等待。
2:Mutex:互斥信號
API:CreateMutex, ReleaseMutex + WaitFor
跟event雷同,然後創建mutex,然後用Waitfor進行檢查是否該資源是否可占用,有則進入並做處理,處理完relaseMutex...
3:Semaphore:信號量
API: CreateSemaphore, ReleaseSemaphore + WaitFor
這個跟上述兩個,有點區別:在創建的時候,指定資源可以有N個,WaitFor時,如果還有資源,則返回有信號,表示占用了一個資源,處理完,必須使用ReleaseSemaphore,將資源返回。
請注意:
以上三種信號,在使用WaitFor,並返回WAIT_OBJECT_0後,表示占用了該資源,處理完成後,需要對應的API進行釋放
event在手動管理(創建時的參數)的情況下,Waitfor不會自動占用資源。
4:Waitfor函數
WaitForSingleObject 或 WaitForMultipleObjects 或 MsgWaitForMultipleObjects。。。
WaitFor是一組類似的等待事件觸發的函數組,具體請查看:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687069(v=vs.85).aspx
一般用用WaitForSingleObject就好了。
此類同步,應該叫觸發類,事件類,通知類的同步。
如:AB線程通過共享一個event/mutex/semaphore,然後線程A置有信號,線程B進行Waitfor等待,達到同步機制。
注意點:
1: event: 不要超過N>2個線程進行對此操作,一般是工作線程中public出來event,
或由該線程本身public一方法去操作置有信號,線程內部本身進行waitfor操作。
2:mutex:同上,我一般用於互斥進程。
3:semaphore: 一般由一個管理者/調度者生成semaphore,然後分配給N個子線程
因為它可以有多個資源(自定義)可占用,有點像XX池資源,但固定只有N個資源,用完只能等待其它線程的返回。
三:PostMessage/SendMessage/PostThreadMessage
這幾個應該很熟悉才對,發送消息到某個線程隊列中。
Post/Send是工作/次線程發送消息到主線程消息隊列。
PostThreadMessage是線程間的消息發送,或主線程發送消息到其它工作線程。
請注意:
PostMessage/PostThreadMessage是有可能發送失敗,如果發送的是指針內存,且“發送後不管”,則會產生內存洩露。
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
OK,有了以上介紹,同步就是件很容易的事
1:同步:主線程->工作線程:
a: 隊列+lock
主線程產生大量工作,且需要工作線程處理,請使用此法
b: event, semaphore置有信號,其它數據,用線程參數賦值(得小心了)(mutex我比較少用)
主線程某條件成熟,不確定周期,通知類的需要讓工作線程工作,請用此法。
c: 使用PostThreadMessage,將需要的東西用參數帶上
請查看VCL代碼:SConnect.pas::TTransportThread.Execute
裡面有Event+PostThreadMessage的處理示例,寫的:你會不由自主的說聲贊。(我將代碼抽出,請查看:示例代碼B)
2:同步:工作線程->主線程:
a: TThread.Synchronize(xxx)
隨著D版本越高,TThread提供的同步函數越來越多,也越來越簡單,居家首選。
b: 使用Post/SendMessage,帶上對應的Handle(Form?Application?)
我喜歡PostMessage,發送後不用管。
我喜歡SendMessage/SendMessageTimeout,相當於同步操作主線程的組件後,繼續下一步。
d: 隊列+lock
在工作線程要產生大量數據,且要主線程處理時,推薦用此法,而不是上述兩法。
c: event, semaphore置信號,通知主線程某條件成熟了...
這裡,不推薦用此法同步,很簡單WaitFor一般用於阻塞式操作,在工作線程用此法居多,而不是在主線程,
雖然可以將WaitFor的timeout=0,以非阻塞操作,但此法不推薦。
3:同步:工作線程間的同步
a: 首選:隊列+lock
不為啥,快且簡單。隊列的push&pop,再加上lock,操作起來簡單快捷。
b: event,semaphore進行互斥訪問
通知性的,資源鎖定類型,可以用它,其實,我更覺得A法更好些,不過有些場合也是有用處的。
c: PostThreadMessage不建議,但也是個法子。
如果你想學習一下線程是怎麼處理消息的,它其實也是簡單的。
請查看:示例代碼A
這裡不做代碼分析,只是說一下線程同步方法及一些適用場合。
其實D.VCL中,裡面的同步法子甚多,比如TThread.Synchronize就很有意思。
沒了。
示例代碼A
procedure TMyThreadA.Execute; var msg: TMsg; begin // 如果使用Peek進行取消息,在取前,必須使用下列函數,進行創建消息隊列,才能使用。 // 如果使用GetMessage,則不需要,但問題是GetMessage會阻塞線程,直到收到消息。 // so,請自行選擇。 PeekMessage(msg, 0, 0, 0, PM_NOREMOVE); while not Terminated do try // 從隊列取消息 while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do begin case msg.message of WM_MYMSG_0: ; // do my msg WM_MYMSG_1: ; // do my msg1 ... else DispatchMessage(msg); end end; // 一般不建議使用WaitMessage,建議使用sleep(5) // 因為退出時,你得發消息,否則Thread.Terminate()線程退不出來 Sleep(5); except // todo: except end; end;
示例代碼B:
procedure TMyThreadB.Execute; var msg: TMsg; begin PeekMessage(msg, 0, 0, 0, PM_NOREMOVE); while not Terminated do try case MsgWaitForMultipleObjects(1, FEvent, False, INFINITE, QS_ALLINPUT) of WAIT_OBJECT_0: begin if not ResetEvent() then // FEvent觸發,需要做處理了。 // 請注意:FEvent一般由其它線程賦值或全局變量 // 且要注意: event創建時,是否手動管理類型的, // 如果是,則要ResetEvent,表示我收到此通知,我干活(注意: resetEvent返回True) end; WAIT_OBJECT_0 + 1: begin // 其它線程發送消息到本線程了,取出來,再根據消息不同進行不同處理。 while PeekMessage(msg, 0, 0, 0, PM_REMOVE) do begin case msg.message of WM_MYMSG_0: ; // do my msg WM_MYMSG_1: ; // do my msg1 ... else DispatchMessage(msg); end end; end; end; except // todo: except end; end;
大概這些,不知想把要說的說完沒。
水平有限,如有雷同,就是盜鏈,:D
2014.11.07 by qsl