程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> delphi.thread.同步,delphi.thread同步

delphi.thread.同步,delphi.thread同步

編輯:Delphi

delphi.thread.同步,delphi.thread同步


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

 

線程同步是個好話題,因為寫線程經常會遇到,所以就寫寫自己知道的東西。 

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  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved