1. 程式人生 > >執行緒處理過程分析

執行緒處理過程分析

本文試著從分析Synchronize同步執行的實現機制入手,來解決DLL/ActiveForm中執行緒同步的問題。
執行緒中進行同步時呼叫的Synchronize函式,僅僅是把呼叫呼叫執行緒、呼叫方法地址、異常物件封裝在一個同步結構中,然後呼叫處理同步結構的類方法Synchronize。
procedure TThread.Synchronize(Method: TThreadMethod);
begin
  FSynchronize.FThread := Self;
  FSynchronize.FSynchronizeException := nil;
  FSynchronize.FMethod := Method;
  Synchronize(@FSynchronize);
end;

class procedure TThread.Synchronize(ASyncRec: PSynchronizeRecord);
var
  SyncProc: TSyncProc;
begin
  if GetCurrentThreadID = MainThreadID then // 如果是主執行緒,當然也就不需要同步處理了,直接執行即可
    ASyncRec.FMethod
  else
  begin
    SyncProc.Signal := CreateEvent(nil, True, False, nil); // 建立一個未命名事件,用於主執行緒執行同步的過程結束時通知等待執行緒
    try
      EnterCriticalSection(ThreadLock); // 進入ThreadLock關鍵區,用於保護對Classes單元的全域性變數SyncList的訪問和進行一些呼叫的同步
      try
        if SyncList = nil then
          SyncList := TList.Create;
        SyncProc.SyncRec := ASyncRec;
        SyncList.Add(@SyncProc); // 把要同步的結構新增到列表SyncList中,等待主執行緒取出執行同步過程程式碼
        SignalSyncEvent; // 呼叫SetEvent(SyncEvent)使同步事件SyncEvent處於訊號狀態,主要用在正確處理執行緒結束時
        if Assigned(WakeMainThread) then
          WakeMainThread(SyncProc.SyncRec.FThread); // 關鍵!Classes.WakeMainThread就是Application.WakeMainThread,通過PostMessage發個WM_NULL空訊息,讓主執行緒醒來
        LeaveCriticalSection(ThreadLock); // 離開關鍵區,因為主執行緒呼叫的過程CheckSynchronize中會在取出SyncList列表同步結構前先進入關鍵區的
        try
          WaitForSingleObject(SyncProc.Signal, INFINITE); // 等待主執行緒同步呼叫結束通知事件
        finally
          EnterCriticalSection(ThreadLock);
        end;
      finally
        LeaveCriticalSection(ThreadLock);
      end;
    finally
      CloseHandle(SyncProc.Signal);
    end;
    if Assigned(ASyncRec.FSynchronizeException) then raise ASyncRec.FSynchronizeException;
  end;
end;
在主執行緒的訊息處理過程WndProc中檢索到WM_NULL訊息就會呼叫CheckSynchronize對同步列表SyncList中的同步結構進行逐個處理,直至SyncList中要同步的方法全部被同步呼叫完畢。而在應用程式空閒時也會呼叫CheckSynchronize進行同步處理。
procedure TApplication.WndProc(var Message: TMessage);
begin
  try
    (略)
    with Message do
      case Msg of
        WM_NULL:
          CheckSynchronize;
      else
        Default;
      end;
  except
    HandleException(Self);
  end;
end;
procedure TApplication.Idle(const Msg: TMsg);
begin
  (略)
  if (GetCurrentThreadID = MainThreadID) and CheckSynchronize then
    Done := False;
  if Done then WaitMessage;
end; 
再看一下Delphi封裝的實際執行緒執行體函式:
function ThreadProc(Thread: TThread): Integer;
var
  FreeThread: Boolean;
begin
  try
    if not Thread.Terminated then
    try
      Thread.Execute;
    except
      Thread.FFatalException := AcquireExceptionObject;
    end;
  finally
    FreeThread := Thread.FFreeOnTerminate; // 是否執行緒執行結束後自動釋放Thread物件例項?
    Result := Thread.FReturnValue;
    Thread.DoTerminate; // 這裡呼叫Synchronize(CallOnTerminate),也就是說OnTerminate事件程式碼實際上是在主執行緒中同步執行的
    Thread.FFinished := True;
    SignalSyncEvent; // 使同步事件SyncEvent處於訊號狀態,使執行緒Destory呼叫WaitFor時如果是主執行緒,可以進行必要的同步處理
    if FreeThread then Thread.Free; // 注意:執行緒Destroy部分的程式碼是線上程執行體中執行的
    EndThread(Result);
  end;
end;

到這裡我們基本清楚了Synchronize同步的實現機制:
執行緒將同步執行緒、方法封裝成同步結構,新增到同步列表SyncList中,然後傳送WM_NULL訊息喚醒主執行緒,然後呼叫WaitForSingleObject掛起等待主執行緒處理。主執行緒處理WM_NULL訊息或在空閒時呼叫CheckSynchronize,從同步列表SyncList取出同步結構,在主執行緒中呼叫執行同步方法,執行完畢通過同步結構中的未命名事件控制代碼通知等待執行緒。等待執行緒收到事件通知後醒來,然後繼續執行。

以上是對通常的應用程式中的執行緒Synchronize同步實現機制的分析,而對於DLL中的執行緒在用Synchronize進行同步時又有它的特殊性。
DLL中的全域性變數是每個DLL都複製一份的,各個DLL之間以及DLL與主程式之間不能直接進行資料共享。這也就是說主程式的SyncEvent、SyncList、ThreadLock與DLL中的SyncEvent、SyncList、ThreadLock變數是不一樣的。因此在DLL中的Synchronize同步,使用到的是DLL中的SyncEvent、SyncList、ThreadLock等變數,因此,直接使用Synchronize,主程式的CheckSynchronize就無法對DLL中的執行緒進行同步排程執行。而DLL中的Application卻從來沒有執行Run進入訊息迴圈,因此也不能呼叫CheckSynchronize來處理執行緒的同步。因此在DLL中就需要主動呼叫CheckSynchronize函式來對同步列表SyncList進行處理。可以在DLL中專門建立一個視窗,在視窗訊息迴圈中處理WM_NULL訊息,呼叫ChechSynchronize即可。
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
  if Msg.message = WM_NULL then
    CheckSynchronize
  else
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;

對ActiveForm中使用的執行緒呼叫Synchronize的情況也大體上相同,它的宿主程序是瀏覽器(如IE),IE一來不是Delphi編的程式,不會在主程式訊息迴圈主呼叫CheckSynchronize進行同步處理的;二來即使它進行同步處理,也不是採用Delphi的同步實現方法,沒有SyncList、SyncEvent、ThreadLock等變數的使用的;另外,ActiveForm由於是個OCX(實際上是種DLL),因此還有如同上述DLL中執行緒同步的問題,解決辦法也可以和上面一樣。當然,應該也可以直接在ActiveForm中新增處理WM_NULL訊息的處理過程,呼叫CheckSynchronize進行同步處理,我沒有進行試驗,具體地,你可以親自試一下。 

相關推薦

執行處理過程分析

本文試著從分析Synchronize同步執行的實現機制入手,來解決DLL/ActiveForm中執行緒同步的問題。執行緒中進行同步時呼叫的Synchronize函式,僅僅是把呼叫呼叫執行緒、呼叫方法地址、異常物件封裝在一個同步結構中,然後呼叫處理同步結構的類方法Synchro

對症下藥:Tomcat停機過程分析執行處理方法

工作中經常遇到因為Tomcat shutdown時自身建立的執行緒沒有及時停止而引起的各種莫名其妙的報錯,這篇文章將通過對Tomcat停機過程的梳理,討論產生這些錯誤的原因,同時提出了兩個可行的解決辦法。 Tomcat停機過程分析 一個Tomcat程序本質上是一個JVM程序,其內部結構如下圖所示:

Java執行洩露的分析處理

1. 生產環境的異常現象及初步分析 最近發現系統程式記憶體消耗越來越大,開始並沒特別注意,就簡單調了一下jvm引數。但直到前些天記憶體爆滿,持續Full GC,這肯定出現了記憶體洩露。 原以為哪裡出現了比較低階的錯誤,所以很直接想到先去看看程式是在跑哪段程式碼。jstac

OTA升級包制作工具處理過程分析

host ext updater 解析 misc dsm 應該 增量升級 預處理 http://blog.csdn.net/ly890700/article/details/56048815 Android Recovery(30) 1、概述 OTA升

執行處理慢sql查詢小筆記~

多執行緒處理慢sql查詢以及List(Array)的拆分 系統資料量不大,但是訪問速度特別慢,使用多執行緒優化一下!!! 優化結果:訪問時間縮短了十幾秒  25s --> 8s 一、List的拆分:Iterables.partition 注意: 引入的包為google名下的   &n

HTML5學習之WebWork多執行處理

  多執行緒技術在服務端技術中已經發展的很成熟了,而在Web端的應用中卻一直是雞肋 在新的標準中,提供的新的WebWork API,讓前端的非同步工作變得異常簡單。 使用:建立一個Worker物件,指向一個js檔案,然後通過Worker

java併發程式設計一一執行池原理分析(三)

合理的設定執行緒池的大小 接著上一篇探討執行緒留下的尾巴。如果合理的設定執行緒池的大小。 要想合理的配置執行緒池的大小、首先得分析任務的特性,可以從以下幾個角度分析: 1、任務的性質:CPU密集型任務、IO密集型任務、混合型任務等; 2、任務的優先順序:高、中、低; 3、任務的執行時

java併發程式設計一一執行池原理分析(二)

2、執行緒池 1、什麼是執行緒池 Java中的執行緒池是運用場景最多的併發框架,幾乎所有需要非同步或併發執行任務的程式都可以使用執行緒池。 在開發工程中,合理的使用執行緒池能夠帶來3個好處。 第一:降低資源的消耗,通過重複利用已建立的執行緒降低執行緒建立和銷燬造成的消耗 第二:提

java併發程式設計一一執行池原理分析(一)

1、併發包 1、CountDownLatch(計數器) CountDownLatch 類位於 java.util.concurrent 包下,利用它可以實現類似於計數器的功能。 比如有一個任務A,它要等待其他4個任務執行完成之後才能執行,此時就可以利用CountDownLatch

JAVA多執行10個執行處理1000個數據

import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import j

Java執行池的認識、常用執行池的分析

什麼是程式,什麼是程序,什麼是執行緒,他們有什麼區別?   程式是指令和資料的有序集合,其本身並沒有任何執行的含義,是一個靜態的概念。 程序是一個動態的過程,是一個活動的實體。簡單來說,一個應用程式得到執行就可以看作是一個程序。程序可以包含多個同時執行的執行緒。程序也是擁有系統

執行處理list

package com.zhx.web.invoice.service; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import

執行記憶體問題分析之mprotect方法【轉】

轉自:https://blog.csdn.net/agwtpcbox/article/details/53230664 http://www.yebangyu.org/blog/2016/02/01/detectmemoryghostinmultithread/ 多執行緒中的記憶體問題,一直被認為是噩夢般

執行池原理分析&鎖的深度化

執行緒池 什麼是執行緒池 Java中的線程池是運用場景最多的並發框架,幾乎所有需要非同步或並發執行任務的程式都可以使用線程池。在開發過程中,合理地使用線程池能夠帶來3個好處。第一:降低資源消耗。通過重複利用已創建的線程降低線程創建和銷毀造成的消耗。第二:提高響應速度。當任務

java併發包&執行池原理分析&鎖的深度化

  併發包 同步容器類 Vector與ArrayList區別 1.ArrayList是最常用的List實現類,內部是通過陣列實現的,它允許對元素進行快速隨機訪問。陣列的缺點是每個元素之間不能有間隔,當陣列大小不滿足時需要增加儲存能力,就要講已經有陣列的資料

[.net 多執行]ConcurrentBag原始碼分析

ConcurrentBag根據操作執行緒,對不同執行緒分配不同的佇列進行資料操作。這樣,每個佇列只有一個執行緒在操作,不會發生併發問題。其內部實現運用了net4.0新加入的ThreadLocal執行緒本地儲存功能。各個佇列間通過連結串列維護。 其內部結構如下:   1、獲取執行緒本地佇列:

python 多執行處理佇列

轉載自: https://blog.csdn.net/u011655220/article/details/79037032 from threading import Thread import time import random from queue import Queue from co

[SimplePlayer] 7. 多執行處理

在前面的文章中,我們分別實現了視訊影象解碼、播放,音訊解碼、播放,現在則需要把這些功能組合起來。總體上來說,整個程式的功能可以分為兩條線路:視訊以及音訊,兩條線之間除了後續的同步操作之外基本沒有任何關聯。而線上路當中,各個模組之間並沒有太緊密的耦合,只要上游模組提供了原料,下游模組就可以執行處理。因此,我們可

IOS儲存多張圖片 多執行處理

儲存多張圖片的時候,既要控制不丟圖片,又要控制圖片可以多執行緒。提高儲存速度,防止程式崩潰,防止使用者等待時間過久 這裡我們可以使用Photos框架的PHPhotoLibrary類,這個可以幫助你多執行緒儲存圖片 -(void)saveBtn { [SSGOTools againRe

通過多執行處理提高Redis效能

Redis通常被稱為單程序單執行緒模型。 這不是真的! Redis還執行多個後端執行緒來執行後端清理工作,例如清理髒資料和關閉檔案描述符。在Redis中,主執行緒負責主要任務,包括但不限於:接收來自客戶端的連線,處理連線讀/寫事件,解析請求,處理命令,處理定時器事件和同步資料。只有一個CPU核心執行單個程