Net-Base Maġazin

15.05.2026

TThread u Synchronize mingħajr deadlocks tal-UI: mudelli robusti għal VCL u Legacy-Code

Kif taħdem b'mod affidabbli ma' TThread, Synchronize u Queue mingħajr ma l-UI tiffriża: kawżi tipċi ta' deadlock, mudell ta' UI-dispatcher prattiku (inkluż timeout), protezzjoni waqt shutdown, strateġiji ta' lock u kontrolli ta' debugging għal applikazzjonijiet Delphi eżistenti u ewoluti.

15.05.2026

Min jaħdem ma‘ threads f‘Delphi, aktar tard jew kmieni jinqeda fuq TThread.Synchronize. U eżatt hemm iseħħu l-affarijiet mhux pjaċevoli: konġelamenti sporadiċi, „UI ma tirrispondix“, deadlocks li jidherom każwali waqt it-twettiq jew waqt il-ftuħ ta‘ dialog. Il-kawża prinċipali rari tkun „Delphi hu miksur“, iżda kważi dejjem hija kombinazzjoni inċonvenjenti ta‘ Synchronize, operazzjonijiet ta‘ stennija li jibbloġġjaw u UI-Thread li ma jikkonsmax il- Message Loop (l-ipproċessar tal-avvenimenti tal-VCL) b’mod nadif. Dan il-post juri mudelli robusti u prattiċi fil-kuntest legacy għal TThread u Synchronize mingħajr UI-Deadlocks – inkluż varjant bil-timeout, trasferiment nadif ta‘ żbalji, regoli ta‘ shutdown u suġġerimenti għall-debugging li jgħinu f’applikazzjonijiet ekzistenti.

Għaliex jinqalgħu deadlocks madwar Synchronize fil-prattika

Synchronize tfisser: worker-thread jaqleb proċedura f’queue li titħaddem fil-Main Thread, u jistenna tipikament sakemm dik il-proċedura tkun tlestiet. F’applikazzjonijiet VCL il-Main Thread huwa fl-istess ħin il-UI-Thread (tiri, controls, avvenimenti). Barra minn hekk, f’ħafna installazzjonijiet hemm oġġetti COM taħt il-STA-Modell (Single-Threaded Apartment: is-sejħiet COM jeħtieġu li jiġu pproċessati fil-istess thread), li jsaħħu l-dependenza fuq Message Loop li jaħdem b’mod ġust.

Deadlocks normalment jseħħu minħabba waħda minn dawn il-konfigurazzjonijiet:

  • WaitFor fil-Main Thread: Il-UI-Thread jistenna għal worker (eż. MyThread.WaitFor), filwaqt li l-worker bħalissa jeħtieġ lill-UI-Thread permezz ta‘ Synchronize. It-tnejn jistennew – tmiem.
  • Lock-Inversion: Il-worker iżomm lock (eż. TCriticalSection jew TMonitor) u jagħmel sejħa għal Synchronize. Il-proċedura sinċronizzata fuq il-UI tipprova tieħu l-istess lock (direttament jew indirettament, spiss permezz ta‘ logging/cache/singletons) – deadlock klassiku.
  • Shutdown/Destroy: Meta tiġi magħluqa form, thread jiġi interrott filwaqt li għad hemm kompiti Synchronize pendenti. Partikolarment perikoluż: sejħiet sinċronizzati jirreferu għall-controls li qed jiġu distrutti.
  • Message Loop blockiert: Dialogs modali, operazzjonijiet UI li jdumu twil, sejħa COM li tibbloġġja jew handler li „mal eben“ jagħmel DB/REST iżommu l-Main Thread bl-addoċċ. Il-kompiti Synchronize jiġu eżegwiti b’rtard jew mhux eżegwiti kompletament.

L-ikbar konsegwenza għall-arkitettura u l-operazzjonijiet: Synchronize huwa saff ta‘ blokk. F’softwer aziendali individwali b’importazzjonijiet, BDE-sostituzzjoni b’konnessjoni nattiva-queries, jobs ta‘ interfaċċa jew servizzi ta‘ sfond b’komponent UI, dan is-saff għandu jkun ikkontrollat b’mod konsċju – inkella ‚rarament‘ jista‘ jsir ‚dejjem, partikolarment meta jkun hemm urġenza‘.

Regola bażika: Il-UI-Thread qatt m’għandu jistenna fuq worker (jekk Synchronize qiegħed jintuża)

Jekk xi worker juża Synchronize, il-Main Thread m’għandux jistenna b’mod qawwi u jibbloġġja fuq dan il-worker. Dan jista‘ jidhir banali, iżda fil-kodiċi legacy huwa waħda mill-ikbar kawżi, għax il-prattiċi bħal „nistennew ftit waqt il-għeluq“ jew „dialog tal-progress jistenna l-konklużjoni“ jiġu implimentati faċilment.

Konsegwenzi prattiċi:

  • L-ebda WaitFor-Aufrufe fil-UI-Thread, ladarba fil-Worker jeżisti triq li tuża Synchronize.
  • Thread-Abschluss per Event/Callback signalisieren: il-UI tibqaʾ responsiv, u tagħmel il-cleanup biss wara l-iskjerament tas-sinjal.
  • Aġġornamenti tal-UI: dejjem poġġihom permezz ta‘ TThread.Queue jew dispatcher, sabiex il-Worker ma jinqabadx.
  • TThread.Queue spiss huwa l-aħjar default: il-Worker jippubblika xogħol lill-Main Thread, ikompli jaħdem u ma jibbloġġjax. Dan jipprevjeni ħafna deadlocks. Ma jsolvix però l-kasijiet limiti – pereżempju meta fil-Worker għandek bżonn zwingend riżultat li jinħolqot fil-Main Thread (eż. aċċess għal riżorsa marbuta mal-UI jew komponent li huwa threadgebunden).

    TThread und Synchronize ohne UI-Deadlocks: Denkmodell für saubere Übergaben

    Mudell ta‘ ħsieb robust huwa: hemm ftit biss trasferimenti sinkroni leġittimi lejn il-Main Thread. Kollox ieħor huwa status, preżentazzjoni jew telemetrija — u għalhekk asinkroniku.

    Diviżjoni sempliċi tgħin fir-reviżjonijiet u fis-stabbilizzazzjoni ta‘ proġetti eżistenti:

    • „Biss wiri“: indikatur tal-progress, linja tal-log, għadd, semafor, attiva/disattiva – dejjem Queue.
    • „Tgaddi l-istat“: il-Worker jipprovdi oġġett tad-data/DTO, l-UI tirrenderja – Queue, iżda b’kopja/immutability (allura l-ebda strutturi li jitbiddlu flimkien).
    • „Il-UI trid tiddeċiedi“: hawn biss tinħtieġ semantika sinkrona (eż. mistoqsija lill-utent). Imbagħad il-mistoqsija ewlenija hi: jeħtieġ tassew li Worker jistenna, jew jista‘ jiġi ristrutturat il-workflow (State Machine, ikkanċella job, kompli aktar tard)?

    Speċjalment it-tielet kategorija hija qafas ta‘ deadlock: jekk il-Worker jistenna riżultat tal-UI, il-UI spiss tkun mċaqalqa biex tistenna lill-Worker (jew indirettament permezz ta‘ locks). Dan jaffettwa ħafna aktar taħt il-piż, ma‘ databases bil-mod jew f’ambjenti Remote-Desktop.

    Source-Schnipsel: UI-Dispatcher mit Queue, optionalem Timeout und sauberem Shutdown

    Il-mudell li ġej jinżamm f’klassi assistiva żgħira għal trasferimenti lejn il-UI. Tiksbu:

    • Post: fire-and-forget permezz ta‘ TThread.Queue (tipiku għal aġġornamenti tal-istatus).
    • Call: sejħa sinkrona b‘ Timeout (mhux komuni, imma utli f’sitwazzjonijiet legacy), mingħajr ma tuża direttament Synchronize bħala punt ta‘ blokk.
    • Shutdown-Schutz: ma taċċettax aktar UI-jobs ġodda, u l-jobs fil-queue jiċċekkjaw flag qabel ma jmissu Controls.

    Klassifikazzjoni teknika: nużaw Queue flimkien ma‘ TEvent (Kernel-Event) għall-feedback. Il-Worker ma jistenna mhux fuq Synchronize, iżda fuq event li jiġi stabbilit fil-Main Thread wara li l-azzjoni fil-queue tkun eżegwita. It-Timeout jipprevjeni stallo ‚li jdum għal dejjem‘ jekk il-UI-Thread, għal xi raġuni, ma jkunx qed jerġa‘ jagħmel il-ħruġ.

    Delphi
    unit UiDispatch;
    
    interface
    
    uses
      System.SysUtils,
      System.Classes,
      System.SyncObjs;
    
    type
      EUiDispatchTimeout = class(Exception);
      EUiDispatchShuttingDown = class(Exception);
    
      /// <summary>
      ///  Kapselt UI-Aufrufe aus Worker-Threads.
      ///  Post: asynchron (Queue).
      ///  Call: synchron mit Timeout, ohne TThread.Synchronize direkt zu blocken.
      /// </summary>
      TUiDispatcher = class
      strict private
        class var FShuttingDown: Integer;
      public
        class procedure BeginShutdown; static;
        class function IsShuttingDown: Boolean; static;
    
        class procedure Post(const AProc: TProc); static;
        class procedure Call(const AProc: TProc; ATimeoutMs: Cardinal = 5000); static;
      end;
    
    implementation
    
    { TUiDispatcher }
    
    class procedure TUiDispatcher.BeginShutdown;
    begin
      TInterlocked.Exchange(FShuttingDown, 1);
    end;
    
    class function TUiDispatcher.IsShuttingDown: Boolean;
    begin
      Result := TInterlocked.CompareExchange(FShuttingDown, 0, 0) = 1;
    end;
    
    class procedure TUiDispatcher.Post(const AProc: TProc);
    begin
      if not Assigned(AProc) then
        Exit;
    
      // Im Shutdown keine neuen UI-Jobs mehr annehmen.
      if IsShuttingDown then
        Exit;
    
      // Queue blockiert den Worker nicht.
      TThread.Queue(nil,
        procedure
        begin
          if IsShuttingDown then
            Exit;
          AProc();
        end);
    end;
    
    class procedure TUiDispatcher.Call(const AProc: TProc; ATimeoutMs: Cardinal);
    var
      DoneEvent: TEvent;
      RaisedObj: TObject;
    begin
      if not Assigned(AProc) then
        Exit;
    
      if IsShuttingDown then
        raise EUiDispatchShuttingDown.Create('UI-Dispatcher ist im Shutdown.');
    
      DoneEvent := TEvent.Create(nil, True, False, '');
      try
        RaisedObj := nil;
    
        TThread.Queue(nil,
          procedure
          begin
            try
              if not IsShuttingDown then
                AProc();
            except
              // Exception-Objekt über die Thread-Grenze reichen.
              // Achtung: Kein "raise" hier, sonst landet es im Main Thread.
              RaisedObj := AcquireExceptionObject;
            end;
            DoneEvent.SetEvent;
          end);
    
        case DoneEvent.WaitFor(ATimeoutMs) of
          wrSignaled:
            begin
              if Assigned(RaisedObj) then
                raise Exception(RaisedObj);
            end;
          wrTimeout:
            raise EUiDispatchTimeout.CreateFmt(
              'Timeout nach %d ms: Main Thread hat UI-Aufruf nicht abgearbeitet.',
              [ATimeoutMs]);
        else
          raise Exception.Create('Unerwarteter WaitFor-Status im UI-Dispatcher.');
        end;
      finally
        DoneEvent.Free;
      end;
    end;
    
    end.

    L-iskop tal-kodiċi u fejn hu b’mod konxju ‚mhux konvenzjonali‘

    Dan il-mudell ma jissostitwixxix kompletament Synchronize, imma jagħmel il-passaggi sinkroni kontrollabbli: il-worker ma jistenniex għall-mekkanika ta‘ Synchronize, iżda jistenna event. B’dan il-mod tista‘ tforza timeouts, tagħti viżibilità fl-operazzjoni li l-UI-Thread huwa mblukkat, u fi fażi ta‘ shutdown tirrifjuta b’mod konsekuttiv kompiti UI ġodda.

    Il-parti ‚mhux konvenzjonali‘ mhix l-Event, iżda d-deċiżjoni li tirrappreżenta s-semantika sinkrona permezz ta‘ Queue + Event. Dan jagħmel sens speċifikament meta jkollok bżonn ittejjeb l-istabbiltà gradwalment f’applikazzjonijiet eżistenti mingħajr ma tiddekonstruixxi immedjatament kull punt ta‘ Synchronize fuq livell arkitettoniku.

    Kundizzjonijiet u ostakli

    • Viżibilità tal-memorja: DoneEvent huwa l-punt ta‘ sinkronizzazzjoni. Dan jagħmel il-qari ta‘ RaisedObj wara WaitFor konsistenti. Madankollu RaisedObj għandu jibqa‘ lokali għal kull chiamata (bħal hawn), qatt globali.
  • Immaniġġjar tal-eċċezzjonijiet: AcquireExceptionObject jipprevjeni li l-eċċezzjoni fil-Main Thread „tisparixxi“. Meta jerġa‘ jittieħed fil-Worker il-Stacktrace mhuwiex identiku għall-oriġini, iżda l-messaġġ ta‘ żball jibqa‘ fil-log tal-Worker, u l-job jista‘ jonqos b’mod nadif.
  • Timeout huwa dijanjosi u protezzjoni: Ma jarranġax Main Thread imblukkat. Iżda jipprevjeni li Worker iżomm riżorsi bla limitu (z. B. BDE-Ablosung mit nativer Anbindung-Transaktionen miftuħa), u jagħmel il-klassi ta‘ żball kejlabbli.
  • Shutdown għandu jibda kmieni: BeginShutdown għandu jkun parti minn sekwenza ċentrali ta‘ Shutdown (z. B. ħafna kmieni f‘OnCloseQuery tal-forma prinċipali). Inkella UI-Jobs għadhom jiġu queued waqt li twieqi jkunu diġà mħassra.
  • Strategija tal-lock: kif tevita inversioni tal-lock bil-UI-Callbacks

    Ħafna deadlocks ma jiġux minħabba WaitFor, iżda minħabba ordni ta‘ lock mhux ċara. Fl-iskennjar tipiku: il-Worker jagħmel lock fuq il-mudell tad-dejta, jisħaq aġġornament tal-UI permezz ta‘ Synchronize, u l-aġġornament tal-UI jerġa‘ jwaqqaf aċċess għall-mudell tad-dejta. Dan huwa loġiku, iżda teknikament fatali.

    Regoli prattiċi li jistgħu jiġu adottati fit-timijiet:

    • M’għandekx iżżomm locks bejn il-fruntieri tat-thread: Qabel ma‘ Worker joġġeġġa jew jis-sinkronizza xi ħaġa lejn il-UI, il-locks funzjonali għandhom ikunu rilaxxati.
    • Il-UI taqra snapshots: Il-UI-Callbacks m’għandhomx jaraw „live“ f’strutturi tal-Worker, iżda għandhom juru kopji/snapshots (z. B. DTO, Record, valuri sempliċi).
    • Il-logging jista‘ jkun kandidat għall-lock: Jekk il-logging juża internament queue, darba-lock fuq fajl jew singleton, jista‘ jsir parti minn deadlock. Il-UI-Callbacks għandhom iżommu l-logging għall-minimu jew jiktebuh fuq pipeline tal-log separata u mhux blokkanti.

    Jekk diġà għandek arkitettura Layer-3 (UI, Services/Domäne, infrastruttura bħal aċċess tad-dejta): idealment il-UI-Callbacks għandhom jagħmlu biss il-UI. Kollox li hu „Service“ m’għandhiex tkun fil-callback. Dan jnaqqas b’mod sinifikanti l-effetti ta‘ reentrancy.

    Shutdown mingħajr tfixkil: „mhux WaitFor, iżda waqfien kooperattiv“

    Fil-ħin tat-tmiem spiss jinqalgħu problemi: il-UI tagħlaq, thread għandu jitlaq, iżda UI-Jobs fil-queue għadhom miftuħa. Shutdown nadif mhux tant «qerda tat-thread», imma żgħira koreografija:

    1. Poġġi flag ta‘ shutdown (z. B. TUiDispatcher.BeginShutdown): Minn issa ‚l quddiem ebda UI-Jobs ġodda.
    2. Waqqaf il-Worker b’mod kooperattiv: Il-Worker jivverifika cancel-flag (z. B. TEvent jew simili TCancellationToken) u jtemm ċikli/Waits.
    3. La tibblokka l-UI: Ebda loop ta‘ stennija qawwi fil-Main Thread. Jekk trid tistenna, nagħmlu dan biss b’loop tal-messaġġi li jkompli jaħdem (jew aħjar: evita kompletament billi ttratta l-kompletament permezz ta‘ callback).
    4. L-aħħar kompiti ta‘ tindif tal-UI biss meta twieqi/controls huma garantiti li għadhom jeżistu. Fil-VCL il-ħin huwa kruċjali: sa l-aħħar mument, meta l-handle ikun imneħħi, jobs queued m’għandhomx imorru fuq il-controls.

    Dan il-proċess huwa rilevanti għall-operazzjoni u l-appoġġ: „L-applikazzjoni tissieq fil-ħin tal-għeluq“ hija problema klassika ta‘ aċċettazzjoni, anki meta l-proċessi teknċi ġew immaniġġjati korrettament. Shutdown definitiv jiffranka hawn ħin reali.

    Debugging: Kif tagħmel il-deadlock viżibbli (mingħajr tbassir)

    Jekk ikun hemm stallo, il-mistoqsija ewlenija hija: Min jistenna min? Xi approċċi li juru effett f’proġetti eżistenti:

    • Inventarjar ta’ kull Wait-post: Tfittxija fulltext għal WaitFor, Sleep fi loopijiet, TEvent.WaitFor, INFINITE. Ħafna problemi huma “Waits” moħbija (anke f’biblioteki).
    • Stato tat-thread fil-log: Ittajpu l-log fil-fruntieri tat-thread: „Job beda“, „UI fil-queue“, „UI eżegwita“, „Job lest“. B’hekk tara jekk il-Main Thread qed jaħdem verament il-jobs li jkunu mqiegħda fil-queue.
    • Iċċekkja suspett ta’ Message Loop: Jekk il-freeze jseħħ biss f’djalogi modali jew f’ċerti interazzjonijiet COM, spiss il-Message Loop huwa l-punt ta’ strozzjatura. L-għan f’dak il-każ: tnaqqis tal-ħidma fuq il-UI-handlers, isolazzjoni tas-sejħiet COM, u ebda operazzjonijiet twal fil-UI.
    • Agħmel Locks viżibbli: Għal TCriticalSection/TMonitor jiswa build ta’ debug b’metadati “Owner” (eż. Thread-ID waqt il-Enter) u kejl taż-żmien. B’hekk tara liema lock il-Main Thread qiegħed iżomm fil-mument, filwaqt li worker jistenna l-UI.

    Importanti hi l-atteġġjament: Deadlocks mhux spiss iseħħu “b’każ”. Huma ċikli deterministiċi li rari jiġu attivati. Ladarba tiddetermina l-ċiklu b’mod ċar, it-tiswija ġeneralment tkun evidenti.

    Varjanti għall-aċċess tad-dejta u għall-jobs tal-interface (FireDAC, REST, sistema tal-fajls)

    Speċjalment ma’ FireDAC (jew aċċessi DB oħra) huwa validu: konnessjoni, transazzjoni u datasets f’prattika huma marbutin ma’ thread. Worker-thread għandu jkollu esklussivament il-kuntest DB tiegħu stess. Sejħiet mill-UI għandhom jiffokaw fuq il-preżentazzjoni, mhux fuq operazzjonijiet DB. Mudell robust huwa:

    1. Il-worker iwettaq Query/REST-call, jikkalkula r-riżultat u joħloq DTO.
    2. Il-worker jippubblika l-DTO permezz ta’ Queue/TUiDispatcher.Post lejn il-UI.
    3. Il-UI tieħu l-DTO u taġġorna l-controls (mingħajr ma tirreferi għal oġġetti tal-worker).

    Jekk għandek forom miksija li nibtgħu historikament (“UI triggert DB, DB-Callback triggert UI”), jiswa disentangling gradwali: l-ewwel isolazzjoni tal-punti ta’ trasferiment (Dispatcher), imbagħad il-migrazzjoni tal-istati fi Services/Model. Dan huwa inqas riskjuż minn rimodellar kbir u jnaqqas id-deadlocks b’mod sinifikanti.

    Konklużjoni: Evitar deadlocks ifisser kontroll tal-trasferimenti

    TThread u Synchronize mingħajr UI-deadlocks mhix teknika waħda biss imma dixxiplina: minimizza blokkaġġi, żomm l-ordni tal-locks, iddefinixxi shutdown u inqas dipendenzi sinkroniċi fuq l-UI. L-UI-Dispatcher muri huwa partikolarment utli f’sitwazzjonijiet legacy, għax juża Queue bħala default, u għall-passijiet sinkroniċi meħtieġa jżid Timeout u regoli ċari ta’ shutdown.

    Jibqa’ limitu tal-użu: Jekk il-Main Thread jibqa’ kontinwament imblukkjat (minħabba loġika UI tqila, katini ta’ djalogi modali jew sejħiet COM-STA), anke dispatcher jista’ biss jiddijanjostika u jeżegwixxi abort kontrollat. Is-soluzzjoni sostenibbli hi tnaqqis tal-piż fuq il-UI u t-taqsim tat-tmexxija tar-responsabilitajiet. Jekk hawn bżonn ta’ appoġġ f’applikazzjoni eżistenti Delphi – mill-imħażen tat-threading sa stabilizzazzjoni gradwali – tista’ torganizza dan il-proġett jew il-modernizzazzjoni: diskussjoni tal-proġett jew tal-modernizzazzjoni ma’ Net-Base.

    Fil-kuntest professjonali, ukoll Delphi Multithreading u Synchronize Deadlock għandhom rwol importanti, meta l-integrazzjonijiet, il-flussi tad-dejta u l-iżvilupp kontinwu għandhom jikkoperaw b’mod nadif.

    Niddiskutu proġett jew iniziattiva ta’ modernizzazzjoni ma’ Net-Base.

    Aqsam il-post

    Aqsam dan il-post direttament

    LinkedIn, X, XING, Facebook, WhatsApp u E-Mail huma immedjatament disponibbli. Għal Instagram nippreparaw il-link u t-test qasir direttament.

    Imejl

    Instagram jiftaħ f'tab ġdid. Il-link u t-test qasir jiġu kkopjati qabel fil-clipboard.