delphi.thread.同步

注意:此文只是講線程間的同步,其它同步不涉及。windows

 

線程同步是個好話題,由於寫線程常常會遇到,因此就寫寫本身知道的東西。 數組

D裏面,同步(特指線程同步)從線程的角度來分,有幾種狀況:函數

  1:主線程與工做線程的同步學習

  2:工做線程與主線程的同步spa

  3:工做線程之間的同步。線程

同步,嗯,直白點講,或能夠說成是:A線程怎麼通知B線程去作某某事,或者說某事須要:如何控制某一時間段內,A能作,B不能作(互斥)。指針

因此,手段很重要,也就有了API,也就有了方法:code

  1:lock(CriticalSection)orm

  2:Event/Mutex/Semaphore + WaitFor
  3:PostMessage/SendMessage/PostThreadMessageblog

 

簡單的作一下介紹,可能不詳細,具體請查看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

相關文章
相關標籤/搜索