Minn suġġett tar-rivista għall-prattika tal-proġett
Paġni ta' servizz u paġni tekniċi relevanti għall-artiklu
Min fil-Delphi jixtieq jparalellizza kompiti b’ħafna kalkolu jew b’ħafna I/O, jispiċċa malajr fuq il-Parallel Programming Library (PPL) u speċifikament fuq TParallel.For. Ir-riżultat huwa spiss viżibbli immedjatament – sakemm fil-istess mument trid aġġorna b’mod «malajr» UI tal-progress. Hawn jiġru l-iffriżar tipiku: UI-freezes f’apparenza każwali, ProgressBar li taqbeż lura, jew deadlock komplet, ladarba tkun qed tmur pass pass fil-debugger.
F’dan l-artiklu nitkellmu dwar Progress-UI threadsikura għal TParallel.For: mudell robust li jaħdem kemm f’VCL kif ukoll f’FMX, li jikkonċentra b’mod nadif l-aġġornamenti tal-UI permezz ta’ TThread.Queue, jikkunsidra Cancel/Abort u jevita b’mod konsekuttiv il-falli ewlenin li jwasslu għal deadlock. Il-fokus hu fuq il-realità ta’ xogħol: mġieba riproduċibbli, responsabbiltajiet ċari u suġġerimenti għad-debug li jgħinuk anki meta l-iżball jinqala’ biss «bei Kunden».
Għaliex il-Progress-UI spiss tinqala’ problemi ma’ TParallel.For
TParallel.For normalment jitħaddem fuq worker-threads mill-pool tat-thread ta’ Delphi. Dawn it-threads m’għandhomx jmissu direkt kontrolli VCL jew FMX, għax il-frameworks tal-UI (Message Loop, Window Handles, Rendering) huma marbuta mar-thread prinċipali. Anki kumment aparentement innocwu bħal ProgressBar.Position := … minn worker jista’ jwassal għal mġieba indefinita: AVs sporadiċi, twieqi mqaxxra jew aġġornamenti li “jifdaw”.
Ir-rimedju ovvju ħafna drabi hu TThread.Synchronize. Dan jissolva l-kwistjoni tal-thread-safety, iżda f’loop-paralleli jwassal malajr għal problema oħra: jinħoloq biċċa bottleneck serjali. Kull worker jistenna lill-thread prinċipali, li min-naħa tiegħu huwa mgħobbi bil-rendering u bil-proporzjon ta’ sejħiet Synchronize. Fuq tagħbija tidher bħal deadlock – anki jekk fil-fatt hu effett ta’ starvation/lockstep.
U mbagħad hemm il-klassi reali tad-deadlock: il-thread prinċipali jistenna (eż. permezz ta’ WaitFor, Task.Wait jew indirettament permezz ta’ sejħiet li jwaqqfuk) sa jtemm l-operazzjoni parallela, filwaqt li l-worker-threads jippruvaw jibagħtu xogħol lill-thread prinċipali permezz ta’ Synchronize jew permezz ta’ użu inapproprjat ta’ Queue. Ir-riżultat: il-thread prinċipali jistenna għall-workers u l-workers jistennew għall-thread prinċipali.
TThread.Queue vs. TThread.Synchronize: der praktische Unterschied
Iż-żewġ mekanismi jintużaw biex kodiċi jitħaddem b’mod sigur fil-thread prinċipali. Id-differenza hi fis-semantika tal-istennija:
- TThread.Synchronize: il-worker li sejjaħ jistenna sakemm il-thread prinċipali jimplimenta l-kodiċi. Dan hu “synchron”, iżid latenzi u huwa ingredjent klassiku għal deadlocks meta l-thread prinċipali jkun imblukkat.
- TThread.Queue: Il-worker jpoġġi l-kodiċi biss f’kju għall-thread ewlieni u jkompli jaħdem. Dan hu „asinkronu“, jiddekupla l-Threads u huwa kważi dejjem l-għażla default aħjar f’sitwazzjonijiet paralleli – sakemm tiġi kkontrollata l-frekwenza tal-aġġornamenti.
Importanti: Queue mhix permess libere. Jekk tibgħat aġġornament tal-Queue f’kull iterazzjoni ta‘ ċiklu, tgħaddas il-Queue tal-thread ewlieni. Allura l-UI mhux se tkun fil-«deadlock», iżda tibqa‘ taħdem bil-mod minħabba n-numru kbir ta‘ messaġġi. Il-UI tidher «tqila», u t-tmiem tal-ipproċessar jintefa‘ lura għax għad fadal mijiet jew eluf ta‘ aġġornamenti tal-UI li għadhom jiġi eżegwiti.
Il-każ estrem li verament jagħmel ħsara: stennija fil-thread tal-UI
F’applikazzjonijiet korporattivi spiss naraw dan il-fluss: ikklikkja fuq il-buttuna „Start“, il-UI tinħall, ProgressDialog jiftaħ, u mbagħad jistenna sinċronament sakemm kollox jintemm, u wara jerġa‘ jattiva. Dan il-mudell huwa l-qalba ta‘ ħafna deadlocks.
Varjanti tipiċi (skond il-bażi tal-kodiċi):
- Il-thread ewlieni jibda TParallel.For u sussegwentement jsejjaħ loġika ta‘ stennija li tibbloċka (direttament jew indirettament).
- ProgressDialog jsejjaħ fil-Constructor jew OnShow rutina li internament tistenna.
- Buttuna ta‘ Annulla tissettja flag, iżda l-thread ewlieni jibqa‘ katschat f’ċiklu ta‘ stennija.
Jekk il-worker-Threads jużaw f’dan il-perjodu Synchronize, deadlock huwa prattikament garantiti. Anke bil-Queue jista‘ jkun hemm qabbad jekk il-thread ewlieni jibqa‘ jibbloċka u ma jipproċessax messaġġi – għaliex allura lanqas il-Queue ma tiġi eżegwita.
Il-konsegwenza operazzjonali hija: il-thread ewlieni m’għandux jibbloċka jistenna għall-loop parallelu jekk huma meħtieġa aġġornamenti tal-UI parallelament. Minflok, il-proċess għandu jkun jew kollu mgħoddi f’task ta‘ sfond, jew trid torganizza tmiem asinkronu (callback/queued azzjoni ta‘ tlestija) li jerġa‘ jippermetti lill-UI fl-aħħar.
Aproċċ nadif: Progress biss aggregat, aġġornamenti tal-UI mnaqqsa
Mudell robust jikkonsisti f’tlett responsabbiltajiet ċar u separati:
- Worker-Threads jagħmlu x-xogħol reali għal kull element/index. Irreportjaw biss il-progress f’forma sigura għat-Threads (kontatur, Queue, Thread-safe Queue).
- Aggregator (spiss: il-thread ewlieni jew timer dedikat fil-UI) jikkalkula minn dak il-progress status għall-UI (pożizzjoni, test, ETA) u jaġġorna l-controls. B’dan tevita aġġornamenti 1:1 għal kull iterazzjoni.
- Konklużjoni (wkoll fil-thread ewlieni): tirrestawra l-UI, turi r-riżultat, tissummarizza l-iżbalji u tirrilaxxa r-riżorsi.
Għaliex din id-diviżjoni taħdem daqstant tajjeb: il-workload jista’ jkun ta’ frekwenza għolja (eluf ta’ elementi), imma l-UI teħtieġ biss ftit aġġornamenti kull sekonda. Fil-prattika biżżejjed 5–10 aġġornamenti/s, u f’impjiegi ħafna mgħaġġla anke 2–4. Kollox ’il fuq minn hekk spiss hu biss storbju viżiv u jikkonsma ħin CPU fil-Message Pump.
Ċensiment threadsigur: Atomiku minflok Lock
Għal ProgressBar sempliċi ħafna drabi jaqbel kontar atomiku. “Atomiku” ifisser: l-inkrement u r-read iseħħu mingħajr Race Condition, tipikament permezz ta’ TInterlocked. B’dan tevita Locks (Critical Sections) fil-hot path tal-loop.
Ideja minima ippruvata:
- Il-kwantità totali tkun magħrufa minn qabel (eż. numru rekord, fajls, IDs).
- Kull iterazzjoni iżżid b’mod atomiku DoneCounter.
- Timer tal-UI jaqra l-counter periodikament u jissettja ProgressBar.Position.
Vantaġġ: L-ebda TThread.Queue għal kull element, l-ebda saturazzjoni tal-UI. Żvantaġġ: m’għandekx messaġġi dettaljati għal kull element (eż. isem fajl). Tista’ żżid messaġġ ta’ status sekondarju u mdawwar għal dik il-funzjonalità (ara l-sezzjoni li jmiss).
Messaġġi ta’ status mingħajr spam: “l-aħħar status jirbaħ”
Jekk trid turi wkoll test qasir (element attwali, fazi, ħtija), għandek bżonn mudell li ma jaffundax l-UI b’kull pass tal-worker. Fil-prattika jaħdem tajjeb il-prinċipju “l-aħħar status jirbaħ”:
- Il-worker jikteb informazzjoni ta’ status f’setup threadsigur (eż. string li jista’ jiġi skambjat b’mod atomiku, jew protett b’lock żgħir).
- Timer tal-UI jieħu periodikament l-aħħar status viżibbli u jpoġġih f’label.
B’hekk il-UI tibqa’ reattiva u xorta tara li qed jiġri xi ħaġa. Importanti hawn mhux tant is-string innifsu imma l-perjodu tal-ħajja: evita li tieħu referenzi għal oġġetti temporanji mill-worker-threads u tgħaddi l-istess referenzi lill-UI-thread. Jekk tgħaddi oġġetti, spjega l-proprjetà b’mod espliċitu.
TParallel.For threadsigura Progress-UI bil TThread.Queue: mudell robust
Hemm xenarji fejn timer tal-UI waħdu mhu jaqbilx: eż. meta trid kunfidenza li fil-aħħar tħaddem eżatt wieħed “Done”-update, jew meta l-aġġornament tal-UI hu pass iktar kumpless (eż. insert f’materjal tal-log, imma mdawwar). F’dak il-każ TThread.Queue jista’ jkun xieraq – imma mhux għal kull iterazzjoni, imma b’mod mirati.
Approċċ prattiku hu Queue biss għal eventi b’frekwenza baxxa:
- Start-Event (ipprepara l-UI, ibblokka l-buttuni)
- Eventi ta’ progress periodiċi (massimu kull X millisekonda)
- Eventi ta’ żball (opsjonali, miġbura)
- Done-Event (irrestawra l-UI, uri r-riżultat)
Il-periodiċità m’għandhiex issir fuq l-UI-thread, iżda diġà fil-kuntest tal-worker: ħalli l-worker jidħol fil-queue biss meta minn tal-aħħar aġġornament tal-UI passa biżżejjed żmien. Għal dan tuża sors ta’ żmien monotonu (eż. TickCount) flimkien ma’ valur atomiku ta’ “last update”.
Importanti: l-aġġornament tal-UI għandu jkun “malajr”. Kalkuli tqil, I/O fuq fajls jew aċċessi għall-bażi ta’ data m’għandhomx ikunu fil-callback tal-UI li jkun ġie mqiegħed fil-queue. Il-callback għandu biss jaqra stati u jissettja controls.
Ħarsien tal-kansellazzjoni: Ikkanċella mingħajr tfixkil
F’applikazzjonijiet reali il-kansellazzjoni mhix fakultattiva. Deċiżjonijiet: Cancel mhuwiex “kill”, imma tmiem kooperattiv. Il-workers għandhom jevalwaw b’mod regolari jekk is-sinjal ta’ kansellazzjoni ġie mċaqlaq u mbagħad joħorġu b’mod nadif. F’Delphi hemm diversi modi għal dan (skont il-kostruzzjoni PPL): flag Volatile proprju, boolean atomiku, jew kunċett ta’ Cancellation permezz ta’ Tasks (skont il-verżjoni u l-arkitettura ta’ Delphi).
Għal l-operat hemm żewġ regoli importanti:
- Il-buttuna Cancel trid tkun viżibbli malajr: Iċċekkja l-flag ta‘ kansellazzjoni f’postijiet raġonevoli, mhux biss fl-aħħar ta‘ iterazzjoni, speċjalment meta l-iterazzjoni tista‘ tieħu sekondi.
- Cancel għandu jneħħi r-riżorsi: Handles miftuħa, fajls temporanji, transazzjonijiet jew locks m’għandhomx jibqgħu. Dan ifisser: F’kull iterazzjoni tal-Worker huma obbligatorji blokki try/finally meta r-riżorsi huma involuti.
Min-naħa tal-UI, Cancel għandu biss jissettja sinjal u jpoġġi l-UI f’status „Stopping…“. Il-konklużjoni vera u r-riattivazzjoni tal-UI iseħħu fil-Done-Event, mhux immedjatament waqt il-klik.
Evita deadlocks: die häufigsten Fallen in der Praxis
Trappola 1: WaitFor/Task.Wait im Main Thread
Meta l-Main Thread ikun blukat, ma jistax jeżegwixxi callbacks ta‘ Queue u ma jproċessa l-messages. Dan jidher bħal deadlock, anke jekk il-Worker jkomplu b’mod korrett. Soluzzjoni: l-ebda waits li jibbloġġjaw fil-UI-Thread. Minflok, poġġi l-azzjoni ta‘ konklużjoni permezz ta‘ TThread.Queue jew kontroll b’event (eż. timer jiċċekkja „lesta“).
Trappola 2: Synchronize innerhalb eines Locks
Klassika: Worker iżomm Critical Section, imbagħad iċempel Synchronize, u fil-UI-callback (direttament jew indirettament) ikun meħtieġ lura l-istess Critical Section. Riżultat: ċirku ta‘ stennija. Ir-regola hija sempliċi: Ebda pass tal-UI (Synchronize/Queue) minn ġewwa lock imżomm. Jekk lock hu meħtieġ, kopja d-dejta kollha f’variabbli lokali, ħalli l-lock, u mbagħad poġġi l-operazzjoni fil-queue.
Trappola 3: UI-Callback jiġġenera reentrancy
Xi drabi l-aġġornament tal-UI innifsu mhuwiex „ħelu“: l-issettjar ta‘ Properties jista‘ jħajjar Events (OnChange, OnResize), li jwasslu għal loġika li taċċessa stati tal-Worker. Dan mhux deadlock fil-aktar sens strett, iżda jwassal għal hangs diffiċli biex tispjega u għal Race Conditions. Soluzzjoni: poġġi l-aġġornamenti tal-UI f’pathijiet “kwieti” (ddiżattiva l-Events temporanjament) jew uża reentrancy-guards (eż. guard atomiku għall-fażi ta‘ aġġornament).
Trappola 4: Zu viele queued Updates
Anke mingħajr Waits il-UI tista‘ „twaqaf“ jekk toħloq għexieren ta‘ eluf ta‘ callbacks mqassma. Sintomi: il-ProgressBar tkompli timbotta ‚il quddiem għal żmien twil, it-tieqa tirreaġixxi bil-mod, u l-CPU fil-Main Thread tkun għolja. Soluzzjoni: tnaqqis tal-velocità (finestra taż-żmien), aggregazzjoni (kontatur), jew struttura verament Producer/Consumer, fejn biss aġġornament wieħed tal-UI jista‘ jkun pending (Coalescing).
Meta jsir aktar kumpless: jiġbor riżultati, jippakkja żbalji, jiggarantixxi ordnijiet
TParallel.For huwa ideali meta l-iterazzjonijiet huma indipendenti. Fil-software tan-negozju l-iterazzjonijiet spiss huma biss „b’mod wiesgħa“ indipendenti: taqraw fajls, sejħu REST-APIs, tikteb ringieli fil-bażi tad-dejta. F’dak il-każ għandek bżonn tippjana sew tliet punti addizzjonali:
- Ġbir ta‘ riżultati li hu thread-sigurtà: Jew per thread buffer lokali (u fl-aħħar jingħaqdu) jew Queue/Collection thread-sigura. Evita locks fil-hot path.
- Immaniġġjar tal-eċċezzjonijiet: l-eċċezzjonijiet mill-Worker-Threads għandhom jiġu miġbura. Fil-prattika jservi tajjeb: iżomm l-ewwel eċċezzjoni u jattiva l-Cancel, jew jiġbor il-kollezzjoni ta‘ eċċezzjonijiet u jurihom flimkien fl-aħħar.
- Ordni: Jekk l-output jeħtieġ ordni stabbli (pereżempju logs skont indiċi), il-proċessar parallelu b’sortjar sussegwenti spiss ikun aktar sempliċi mill-„inseriment ordinat thread-safe“.
Għall-UI dan ifisser: ma turi każ ta‘ żball immedjatament. Dan iwassal għal infern ta‘ dialogi modali. Iġbor l-iżbalji (pereżempju lista ta‘ stringi) u urihom fl-aħħar bħala sommarju jew log esportabbli.
Debugging: Kif tagħmel id-deadlock tassew viżibbli
Deadlocks fil-kodiċi parallelu huma frustrazzjoni għax jistgħu jidhru differenti fid-debugger meta mqabbla mar-release. Madankollu hemm ċerti leveri prattiċi:
Uża t-tieqa tal-Threads u l-Call Stacks
Jekk l-UI tistagna, eżamina l-ħjut kollha: fejn qiegħed il-Main Thread? Qed jistenna? Huwa f’message-loop? Fejn qegħdin il-Worker-Threads? Jekk il-Worker huma staggmati f‘Synchronize, il-kawża kważi dejjem tkun „Main Thread imblukkat“ jew „Main Thread jeħtieġ Lock“.
Marka l-punti tal-Queue/Synchronize
Poġġi logging mirqum fil-punti ta‘ trasferiment (qabel il-Queue, fil-Queue-callback, fit-tmiem tal-iterazzjoni). Fil-produzzjoni dan spiss ikun aktar prezzjuż mill-breakpoints, għax il-timing huwa deċiżiv. Żgura li l-logging innifsu jkun thread-safe u non-blocking (per eżempju, ebda output tal-log għall-UI direttament mill-Worker-Threads).
Imkejjel il-ħin tal-last-update
Jekk l-UI „tistagna“, jista‘ jkun sempliċement li għandha wisq aġġornamenti biex tipproċessa. Għalhekk imkejjel fil-Main Thread kemm drabi fis-sekonda twettaq UI-update u kemm idum. Ladarba l-UI-callbacks jieħdu aktar minn ftit millisekondi, għandek bżonn throttling jew semplifikazzjoni.
Kemm hu utli TParallel.For b’Progress-UI verament?
Il-parallelizzazione mhix skop għalih innifsu. Tibbenefika speċjalment meta:
- l-iterazzjonijiet huma biżżejjed kbar (millisekondi sa sekondi) sabiex l-overhead tal-threadpool jiġi assorbit,
- ix-xogħol huwa intensiv fuq il-CPU (Parsing, kompressjoni, Hashing) jew għandu I/O faċilment parallelizzabbli (diversi fajls, diversi HTTP-Requests b’limiti),
- għandek strateġija ċara għall-Cancel u għall-maniġġjar tal-iżbalji,
- ir-rekwiżiti tal-UI jistgħu jingħataw b’progress aggregat.
Tibbenefika inqas meta kull iterazzjoni tkun estremament qasira (mikro-operazzjonijiet) jew meta l-iterazzjonijiet kollha joperaw fuq l-istess punt ta‘ strozzatura (transazzjoni DB seriali, lock globali, fajl wieħed). F’dak il-każ, l-intervent aktar mgħaġġel spiss ikun: ittejjeb l-algoritmu, uża batch processing, tnaqqas l-aċċess tad-dejta jew iddikoppja espliċitament il-punt ta‘ strozzatura.
Checklista tal-prattika: Kif tibqa‘ l-UI stabbli
- Main Thread mhux imblukkat: ebda waits, ebda loops twal mingħajr message pump.
- Il-Worker qatt ma jikkontrollaw il-controls: ebda access VCL/FMX barra mill-UI-Threads.
- UI-updates huma throttled: uża counter/timer jew coalesced Queue-updates minflok kull iterazzjoni.
- Ebda chiamata Synchronize minn ġewwa locks.
- Il-Cancel huwa kooperattiv, jiġi kkontrollat spiss u jagħmel tindif nadif.
- L-eċċezzjonijiet jiġu miġbura u jiġu pproċessati b’mod ordnat fl-aħħar.
Konklużjoni: Iddekoppelja fil-Queue, stabbilizza bl-aggregazzjoni
UI ta‘ progress robusta f‘TParallel.For ma tinħolqx billi tpoġġi „Synchronize“ xi mkien, iżda permezz ta‘ prinċipju ta‘ arkitettura ċar: il-Worker jaħdmu b’mod indipendenti, il-Main Thread jibqa‘ ħieles u jipproċessa biss ftit u mgħaġġla aġġornamenti tal-UI. TThread.Queue huwa l-għodda t-tajba jekk tużah b’mod mmirat u b’kontroll tal-frekwenza. Għal mġieba „bil-mod“ jew instabbli huma kważi dejjem responsabbli żewġ kawżi: il-Main Thread jistenna b’mod li jibbloċka f’post wieħed – jew jinġeraq f’ħafna aġġornamenti mqiegħda fil-queue.
Meta issettja l-mudell b’mod nadif (Counter/Coalescing, Cancel-Flag, callback tat-tlestija), jiswa li jintuża f’ħafna partijiet ta‘ applikazzjoni Delphi żviluppata maż-żmien: Import/Export, verifiki tad-dejta, kompiti ta‘ fajl u tal-API – kollox isir aktar reattiv mingħajr ma toħloq deadlocks ġodda għal kull aġġornament tal-progress.
Jekk għandek bżonn appoġġ biex tistabbilizza kodiċi parallelu, biex tdebuggja problemi ta‘ freeze tal-UI jew biex timmodernizza b’mod nadif applikazzjonijiet Delphi mibnija maż-żmien: ikkuntattjana.
Għal dan it-tema huma importanti wkoll Delphi Parallel Programming Library u Tthread.queue Vs Synchronize. Il-kariga tpoġġi dawn l-aspett f’kuntest b’mod ċar u turi x’hu rilevanti fil-prattika ta‘ kuljum.
Tiddiskuti proġett jew inizjattiva ta‘ modernizzazzjoni ma‘ Net-Base.
Pass li jmiss
Meta suġġett jiġi mwettaq bħala proġett reali, l-arkitettura, is-sistema eżistenti u l-operat għandhom jiġu kkunsidrati flimkien kmieni.
Aħna nappoġġjaw mhux biss f'kwistjonijiet puntwali, iżda wkoll meta biċċiet ta' kodiċi sors, temi legacy jew ideat għal portali jridu jsiru proġett korporattiv stabbli u affidabbli.
- L-istat attwali, l-istat tal-mira u r-riskji tekniċi jiġu vvalutati flimkien.
- REST, aċċess tad-dejta, portalijiet u rollout ma jiġu posposti bħala konsegwenzi tardivi.
- Tara kmieni liema triq hija ekonomika u operattivament sostenibbli.