Net-Base Dergi

02.08.2026

Windows Servisi Delphi içinde: TEvent ve Stop-Timeout ile Graceful Shutdown'u doğru şekilde uygulamak

Bir Windows servisi durdurulurken takılıp kalıyorsa, bu nadiren tesadüftür: Çoğunlukla thread'ler, I/O işlemleri veya çıkış yolu olmayan Sleep döngüleri bloke olur. Bu pratik yazı, Delphi içinde TEvent ile temiz bir Graceful Shutdown uygulamayı ve Stop-Timeouts'u doğru şekilde ele almayı gösteriyor...

02.08.2026

Dergi konusundan proje pratiğine

İçeriğe Uygun Hizmet ve Teknik Sayfalar

Bir Windows Service in Delphi günlük kullanımda çoğu zaman gösterişsiz görünür: arka planda çalışır, görevleri işler, log yazar, veritabanlarına veya REST API’lerine erişir. Ta ki biri „Hizmeti durdur“ tıklayana — veya bir yama yeniden başlatması gerektiğinde — ve servis düzgün şekilde durmaz. O zaman Hizmetler konsolu dakikalarca „Durduruluyor…“ gösterir, servis Stop Pending durumunda takılır ve en kötü durumda süreç zorla sonlandırılır. Tam da bu noktada, Windows Service in Delphi Graceful Shutdown konusunu bilinçli bir mimari mesele olarak ele almak işe yarar: net bir shutdown sinyali, tanımlanmış timeout’lar ve gerçekten tepki veren thread’ler ile.

Bu yazıda framework iç detaylarından değil, pratikte uygulanabilir bir desenden söz ediliyor: TEvent Stop-Sinyali olarak (kernel’e yakın bir senkronizasyon nesnesi olan System.SyncObjs içinden), bununla birleştirilmiş bir Stop-Timeout stratejisi; bu strateji hem Windows Service Control Manager (SCM, yani hizmetleri başlatıp/durduran Windows bileşeni) hem de kendi Worker-Thread’lerinizi dikkate alır. Ayrıca tipik sınır durumları, hata ayıklama yaklaşımları ve ek mantığın ne zaman gerçekten faydalı olduğu sorusu ele alınacak.

Windows Service in Delphi Graceful Shutdown pratikte

En sık rastlanan neden basittir: Serviste en az bir thread vardır ve bu thread bloklayıcı bir işlemde takılı kalmıştır ve bir kesme/iptal yolu yoktur. Yaygın örnekler:

  • Sleep ile polling döngüleri: „while not Terminated do Sleep(1000)“. Durdurma geldiğinde sinyal ulaşır, ancak thread yalnızca 1 saniye sonra (veya 30 saniye…) tepki verir.
  • Bloklayıcı G/Ç: Veritabanı çağrıları, HTTP istekleri, Named Pipe’lar, dosya sistemi beklemeleri — Stop sinyaline dikkat etmeyen, „sadece bekleyen“ her şey.
  • Wakeup olmayan kuyruk tüketicileri: Bir worker bir kuyruğu bekler, ancak durdurma sırasında çıkması için uyandırılmaz.
  • Lock sıralaması/Deadlock’lar: Durdurma sırasında „cleanup“ yapılırken diğer thread’ler hâlâ kilit tutuyor olabilir. Bu genellikle yalnızca durdurma yolunda ortaya çıkar, çünkü oradaki kilit edinme sırası normal işletimdekinden farklıdır.

Windows SCM, bir servisin bir Stop komutuna hızlıca yanıt vermesini ve durumunu sürekli bildirmesini bekler (via SetServiceStatus; Delphi bunu Service bileşeninde kapsüller). Eğer bir Stop olayı alıyor ancak thread’lerinizi düzgün şekilde kapatmıyorsanız süreç canlı kalır — ve Windows eninde sonunda „çok uzun sürdüğüne“ karar verir. Sonuç ya sert bir sonlandırma olur ya da belirsiz bir ara durumda takılı kalan bir servis.

Temel ilke: Her Worker’ın anlayacağı bir Stop-Sinyali

Abstrakte Grafik: Worker-Threads warten auf Work oder Stop-Event und beenden geordnet
Worker’lar „Work veya Stop“ beklediğinde, polling olmadan durdurma gecikmesi azalır.

Bir Graceful Shutdown ancak şu koşullar sağlandığında işler: bir sinyalin olması ki:

  • tüm ilgili thread’ler tarafından gözlemlenebilir,
  • auch aus blockierenden Wartezuständen heraus Wirkung entfaltet,
  • im Stop-Pfad deterministisch ist (keine „vielleicht kommt er irgendwann raus“-Hoffnung),
  • eine klare Timeout-Strategie hat.

In Delphi ist TEvent dafür ein sehr brauchbares Werkzeug: ein Event-Objekt, das intern über Windows-Handles umgesetzt ist (vergleichbar mit CreateEvent/SetEvent). Du kannst es als „Stop requested“-Signal verwenden. Jeder Worker wartet dann nicht einfach blind, sondern wartet „auf Arbeit oder auf Stop“.

TEvent richtig wählen: ManualReset vs. AutoReset

Bei Stop-Signalen willst du üblicherweise Manual Reset (manuell zurücksetzbar): Einmal gesetzt, bleibt das Event „signaled“, bis du es zurücksetzt. Damit ist sichergestellt, dass jeder Thread, der später in eine Wartephase kommt, das Stop-Signal trotzdem erkennt. Auto Reset wäre hier riskant, weil es das Signal nach einem wartenden Thread automatisch zurücksetzt und andere Threads das Stop-Signal verpassen könnten.

Delphi-Service-Lebenszyklus: Wo Stop wirklich ankommt

Ein Delphi-Windows- und Linux-Services basiert typischerweise auf TService (VCL/RTL). Der SCM schickt Kommandos (Start, Stop, Pause, Continue). Delphi ruft dann entsprechende Events/Methoden auf (je nach Template z. B. OnStart, OnStop, OnExecute).

Wichtig für die Architektur:

  • OnStop ist kein Ort für langes Warten ohne Status-Updates. Es ist der Ort, an dem du den Shutdown anstößt und dann kontrolliert wartest – mit Timeout.
  • OnExecute ist oft eine Schleife. Wenn du dort „endlos“ arbeitest, muss die Schleife auf ein Stop-Signal reagieren.
  • Worker-Threads (TThread oder Thread-Pools) müssen auf dasselbe Stop-Signal reagieren, sonst ist der Service logisch gestoppt, aber physisch noch nicht fertig.

Sauberes Muster: Stop-Event + Join der Worker + harter Fallback

Das praxistaugliche Muster besteht aus vier Schritten:

  1. Stop anfordern: Stop-Event setzen, keine neuen Jobs mehr annehmen.
  2. Wakeups auslösen: Wenn Worker auf Queues oder Sleeps warten, müssen sie „aufwachen“ können (z. B. über Event/Queue-Signal).
  3. Geordnet beenden: Worker beenden ihre Loops, schließen Ressourcen (DB-Verbindungen, Dateien, Handles) und melden „fertig“.
  4. Timeout und Fallback: Wenn nicht alles rechtzeitig endet, musst du eine Entscheidung treffen: weiter warten (mit Status-Update) oder kontrolliert abbrechen/hart beenden (je nach Risiko).

Der Kern ist: kein Thread darf ausschließlich auf Zeit warten (Sleep) oder ausschließlich auf I/O blockieren, ohne parallel ein Stop-Signal zu berücksichtigen. Stattdessen arbeitest du mit Wartefunktionen, die mehrere Signale berücksichtigen (z. B. „Stop-Event oder Work-Event“), oder du kapselst I/O in Timeouts plus Stop-Checks.

Stop-Timeout richtig denken: SCM-Timeout vs. eigener Shutdown-Timeout

Hier passieren in Projekten die meisten Missverständnisse. Es gibt zwei verschiedene Timeout-Ebenen:

  • SCM-Erwartung: Windows bekler ki SERVICE_STOP_PENDING durumundayken düzenli olarak ilerleme raporlayasınız. Aksi halde takılı kalmış gibi görünürsünüz. Delphi bunun bir kısmıyla ilgilenir, ancak kendiniz daha uzun süre bloklanırsanız, durum güncellemelerini sürdürmenin (veya durdurma fazınızı kısa tutmanın) bir stratejisine ihtiyacınız olur.
  • Dein eigener Shutdown-Timeout: Örneğin şu tanımı yaparsınız: „Çalışan işleri düzgün kapatmak için kendimize 20 saniye veriyoruz, sonra iptal ediyoruz.“ Bu bir mimari karardır: veri tutarlılığı vs. zorunlu yeniden başlatma vs. işletme gereksinimleri.

Pratikte bunun anlamı şudur: Servisiniz hızla yeni iş birimleri başlatmayacağı bir duruma geçmeli ve ardından sadece devam eden işin bitmesini beklemeli — ama sonsuza kadar değil. Bu bekleme fazı küçük aralıklarla çalışmalı, böylece müdahale edebilir ve gerekirse loglayabilirsiniz.

Wie lang darf der Stop dauern?

Her zaman uyan sihirli bir sayı yoktur. Birçok iş servisi için bir hedef aralığı olarak 5–30 Sekunden gerçekçidir: „in-flight“ veriler için yeterli zaman, ancak bakım/paketleme penceleri için kısa. Düzenli olarak daha uzun süreye ihtiyacınız oluyorsa, bu genellikle çok büyük birimleri tek parça halinde işlediğinizin veya dış bağımlılıkların (DB/HTTP) timeout olmadan çalıştığının göstergesidir.

Implementierung mit TEvent: Aufbau, der im Betrieb stabil bleibt

Delphi-servisinde denenmiş bir yapı şu şekildedir (framework detaylarını zorlamadan):

  • Bir Stop-Event (TEvent, Manual Reset), durdurma sırasında set edilen bir olay.
  • Bir veya birkaç Worker-Threads, ana döngülerinde düzenli olarak Stop kontrolü yapan.
  • Opsiyonel olarak bir Work-Event veya işi işaret eden bir kuyruk. Workerlar bu durumda „Work veya Stop“ için beklerler.
  • Workerları „join“ eden bir Shutdown-Phase (yani bitmelerini bekleyen), ancak zaman aşımı olan bir kapanış evresi.

Önemli olan TThread, omnithreadlibrary veya kendi havuzunuzu kullanıp kullanmamanız değil; workerlarınızın „kör“ şekilde çalışmaması gerekir. Bir worker döngüsü yapısal olarak şöyle görünmelidir: Ereğini(leri) bekle → küçük parçalarda iş yap → parçalar arasında Stop kontrol et → kaynakları temiz şekilde serbest bırak.

Fallstrick: Terminate allein reicht nicht

Birçok Delphi-thread Terminate ile „sonlandırılır“. Ancak bu sadece bir flag’tir. Thread o sırada bloklayıcı bir API çağrısında ise, ilk etapta hiçbir şey olmaz. Bu yüzden ayrı bir Stop-Event çok faydalıdır: onu bekleme çağrılarına entegre edebilir ve hedefli wakeup’lar tetikleyebilirsiniz.

Fallstrick: FreeOnTerminate im Service-Kontext

Servislerde sıklıkla FreeOnTerminate := True görülür. Bu çalışabilir, ancak kapanışı kontrol etmeyi zorlaştırır çünkü genellikle thread sonunu beklemek ve hata durumlarını kaydetmek için temiz bir referansınız kalmaz. Kontrollü bir stop mantığı için çoğu zaman threadleri açıkça sahiplenmek ve shutdown sırasında deterministik olarak bekleyip serbest bırakmak daha stabildir.

Blockierende Operationen: So machst du sie stop-fähig

Sorun giderme sahnesi: Ağ bağlantısı, engelleyen çağrılar ve zaman aşımı nedenidir
Zaman aşımı olmayan engelleyen I/O, servisteki Stop takılmalarının en sık karşılaşılan nedenidir.

Zor olan kısım etkinliğin (Event) kendisi değil, servisinin tıkandığı yerlerdir. Üç tipik sınıf:

1) Sleep/Polling yerine: Stop-Event ile Wait

Periyodik çalışıyorsan (ör. „her 10 saniyede kontrol“), Sleep(10000) kullanma; bunun yerine timeout ile bir Event bekle. Böylece Stop-Event beklemeyi hemen sonlandırabilir. Bu, Stop gecikmesini azaltır ve „Servis yanıt vermiyor“ hissini önler.

2) Kuyruk-Tüketicisi: Work-Event + Stop-Event kombinasyonu

Producer/Consumer mimarisi varsa (örn. işler bir kuyruğa konur), tüketiciyi uyandıracak bir sinyale ihtiyacın vardır. Genellikle bu ek bir TEvent (Work available) olur. Tüketici sonra iki handle bekler: „Work“ veya „Stop“. Stop sırasında Stop-Event’i ve gerekirse Work-Event’i setlersin, böylece tüm tüketiciler kesin olarak Wait’ten çıkar.

3) Harici Çağrılar (DB/HTTP): Zaman aşımları ve iptal yolları

Veritabanı erişimlerinde veya HTTP çağrılarında servisinin düzgün kapanıp kapanmayacağı belli olur. Operasyon için kural: Zaman aşımı olmayan çağrı yok. Zaman aşımı lüks değil, kontrol edilebilirliğin önkoşuludur. Ayrıca deneme/geriçekilme (Retries/Backoff) aşamaları arasında hep Stop kontrol etmelisin. Aksi halde klasik durumu yaşarsın: „Servis kapanmıyor çünkü şu anda 10 deneme ile Sleep yapıyor“.

Bazı kütüphanelerde iptalleri açıkça tetikleyebilirsin (örn. Query-Abbruch). Bu mümkün değilse en azından Timeouts’u shutdown-timeout’u aşmayacak kadar kısa olacak şekilde yapılandırmalısın.

Stop Pending doğru: Durum, Loglama ve Beklenti Yönetimi

Zaman ölçümü ve log analizi ile Windows servislerindeki Stop-Timeout'ların teşhisi
Aşama logları ve zaman ölçümü, Stop-Timeout’ları yeniden üretilebilir ve açıklanabilir kılar.

Bir servis durduğunda, operasyon açısından nerede takıldığı anlaşılmalıdır. Bunun için iki şeye ihtiyacın var:

  • Log-Marker stop yolunda: „Stop talep edildi“, „yeni iş yok“, „Worker bekleniyor“, „Worker X sonlandı“, „Shutdown tamamlandı“.
  • Ölçülebilir süreler: Stop ne kadar sürüyor? Hangi aşama zamanı yiyor? Burada çoğunlukla monoton bir zaman ölçüsü yeterlidir, ör. GetTickCount64 veya TStopwatch (monoton = sistem saati değişikliklerinden etkilenmez).

Eğer Stop yolunda sadece tek bir log girdisi „Stopping…“ yazarsan, sahada hata ayıklama bir tahmin oyununa dönüşür. Servis işletiminde loglar genellikle etkileşimsiz elde edebileceğin tek şeydir.

Servislerde hangi loglar gerçekten yardımcı olur?

  • Service-PID, başlama zamanı, sürüm/build (aşırı yük olmadan).
  • Aktif worker sayısı, in-flight iş sayısı.
  • Aktif dış bağımlılıklar: „DB-Call läuft“, „HTTP-Request läuft“, „Dosya-Flush läuft“ (sadece toplu halde, her ayrıntı değil).
  • Stop-Timeout ulaşıldı: hangi worker’lar hâlâ açık?

Sahada hata ayıklama: tahmin yerine yeniden üretilebilir hale getirmek

Stop sorunları genellikle yalnızca üretimde ortaya çıkar: farklı yük, farklı gecikmeler, farklı izinler, farklı yama zaman pencereleri. Birkaç pratikte denenmiş müdahale:

Servisi kontrol altında test etmek

  • Aktif işleme sırasında durdurma (idle durumda değil).
  • Dış bir arıza sırasında durdurma: DB kısa süreyle erişilemez, HTTP uç noktası yavaş, dosya paylaşımı kayboldu.
  • Başlatmayı takiben hemen durdurma (race-condition’lar: worker’lar hâlâ kuruluyor).

Event Viewer und Service Control Manager sinyalleri

Windows hizmet olaylarını kaydeder, ancak genellikle kaba olur. Daha iyi olanı, servisin kendi log dosyasına veya Windows Event Log’a yazmasıdır. Önemli olan şudur: Stop yolunda logging hâlâ çalışıyor olmalıdır. Logger’ı shutdown sırasında çok erken serbest bırakırsan ya da flush bloke olursa, tam da kritik izleri kaybedersin.

Asılı kalan thread’leri görünür kılmak

Tekrar tekrar „Stop Timeout“ görüyorsan, thread durumlarına bakmak (ör. test ortamında Debugger/Procdump ile) faydalıdır. Çoğu zaman bir thread, asla sinyallemeyen bir handle üzerinde wait durumunda ya da timeout olmayan bir ağ çağrısında bulunur. Düzeltme nadiren „daha fazla sleep“ olur; genellikle temiz bir sonlandırma yolu gereklidir.

Bu çaba gerçekten ne zaman yapılmaya değer?

Sadece bir zamanlayıcıya sahip ve dış bağımlılığı olmayan minimalist bir servis bazen „basitçe durabilir“. Ancak aşağıdakilerden biri söz konusuysa, düzgün bir kontrollü kapanış (graceful shutdown) neredeyse her zaman değerlidir:

  • Servis yan etkili işler işliyorsa (dosya yazma, DB-işlemleri, API çağrıları).
  • Birden fazla thread veya bir havuz varsa.
  • Servis ağ kaynaklarına bağlıysa (DB, REST, Message Broker, dosya paylaşımları).
  • Operasyon planlanabilir bakım pencereleri talep ediyorsa (yeniden başlatmalar, güncellemeler, failover).

Katma değer „zerafet“ değil, operasyon güvenliğidir: daha az sert süreç sonlandırması, daha az tutarsız ara durum, daha az manuel müdahale.

Uygulama tuzakları: Shutdown sırasında sıkça neyin yanlış gittiği

1) Stop tetikleniyor, ama yine de yeni işler geliyor

Eğer gelen işleri kabul ediyorsan (örn. socket, dosya tetikleyici, zamanlayıcı aracılığıyla), Stop yolunda önce yeni iş kabulünü durdurmalısın: listener’ı kapat, zamanlayıcıyı devre dışı bırak, scheduler’ı durdur. Aksi halde hep yeni işler başladığı için sonu yakalayamazsın.

2) Cleanup blockiert (Flush, Close, Finalize)

„Hemen her şeyi flush edelim“ serviste tehlikeli olabilir; hedef (ağ sürücüsü, uzak log, DB) o sırada takılıysa. Bu yüzden: cleanup yapın, ama sınırlı süre ile. Gerekirse, tüm stop’u engellemek yerine hangi verileri hafızada (in-memory) kaybedeceğinize karar vermelisiniz.

3) Kilitler und Reihenfolge

Stop sırasında genellikle Worker’ların da eriştiği aynı veri yapılarına başvurursun (kuyruklar, önbellekler, durumlar). Eğer Stop iş parçacığı kilitler (Locks) tutuyor ve Worker’ların sona ermesini beklerken Worker’lar aynı kilide ihtiyaç duyuyorsa, bir Stop-kilitlenmesi (deadlock) oluşur. Gegenmittel: Kilit tutma sürelerini kısa tut, Stop yolunda kilit altında „bekleme“ yapma, net bir sıra tanımla.

4) Yinelemeli Stop sırasında eşzamanlılık

Pratikte Stop birden fazla kez tetiklenebilir (ör. Stop + Shutdown veya Stop yeniden gelir). Stop yolun idempotent olmalıdır: Stop olayını ayarlamak sorun değil, ancak çift Join/Free mantığı temizce korunmalıdır (ör. bir atomik bayrak ile).

Operatif bakış: Service’ten Admin’ler ve IT-Leads ne bekler

İşletme ve yönetim açısından sonunda önemli olan kodun ne kadar „güzel“ olduğu değil, hizmetin:

  • Stop edildiğinde güvenilir şekilde sonlanması (öngörülebilir, takılmadan),
  • Stop sırasında tutarsız veri üretmemesi (ör. yarım kalmış dosyalar, açık transaction’lar),
  • hata durumunda kullanılabilir loglar sağlaması,
  • bakım pencereleri ve dağıtımlarda hesaplanabilir olmasıdır.

Bu yüzden Stop-Timeout konusu sadece „geliştirici meselesi“ değildir: Yamalama döngülerini, kurtarma sürelerini ve otomatik dağıtımların mümkün olup olmadığını etkiler.

Sağlam bir shutdown tasarımı için somut kılavuzlar

Konuyu pragmatik olarak standardize etmek istersen, şu kılavuzlar işe yarar:

  • Global bir Stop-Event, Manual Reset, servis yaşam döngüsünün erken aşamasında oluşturulmuş, geç serbest bırakılmış.
  • Worker döngülerinde Sleep yok; Stop dostu alternatif (zaman aşmalı Wait) kullanılmalı.
  • Tüm dış çağrular zaman aşımıyla (DB, HTTP, dosya paylaşımları). Zaman aşımını Shutdown-Timeout içine sığacak şekilde seç.
  • Stop-Timeout konfigürasyon olarak (ör. INI/Registry), böylece işletme yeniden derleme yapmadan müdahale edebilir.
  • Aşamalı model: Önce graceful (çalışan işleri bitir), sonra isteğe bağlı „soft abort“ (yeni adımlar yok), en son çare olarak sert exit.
  • İyi Stop logları fazlar ve zaman ölçümleriyle.

Sonuç: TEvent + Stop-Timeout lüks değil, kontrol edilebilirlik

Asılı kalan bir Stop nadiren tekil bir hata olur – genellikle bir mimari gedik vardır: İş iş parçacıklarında veya ortak Stop sinyalini tanımayan bloklayıcı çağrılarda yürür. Net bir Stop-Event (TEvent, Manual Reset), Sleep yerine stop-dostu Wait’ler, dış bağımlılıklar için tutarlı zaman aşımı uygulamaları ve tanımlı bir Shutdown-Timeout ile günlük kullanımda öngörülebilir bir servis elde edersin.

Bu kod, servisiniz üretim ortamlarında bakım pencereleri, otomatik dağıtımlar veya kritik yan etkilerle çalışıyorsa özellikle değer kazanır. O zaman „Graceful Shutdown“ kozmetik değil, istikrarlı işletim ve bir sonraki yeniden başlatmadaki daha az eskalasyon için bir yapı taşıdır.

Eğer Stop yolunuzu bir kere düzgün kurmak veya mevcut bir Delphi servisini sağlam shutdown mantığı ve işletim güvenliği açısından gözden geçirmek istersen, teknik bir sparring-call genellikle hızlıca net adımlar sunar: İletişime geçin.

Bu konu için Delphi Windows Service ve Tevent Delphi de önemlidir. Yazı bu konuları anlaşılır şekilde konumlandırır ve günlük kullanımda nelere dikkat edilmesi gerektiğini gösterir.

Projekt oder Modernisierungsvorhaben mit Net-Base besprechen.

Sonraki adım

Konu gerçek bir projeye dönüştüğünde, mimari, mevcut sistemler ve işletme erken dönemde birlikte değerlendirilmelidir.

Bireysel sorularda destek vermekle kalmıyoruz; kaynak kodu parçacıklarından, legacy konularından veya portal fikirlerinden sağlam bir kurumsal projeye dönüşene kadar da destek veriyoruz.

  • Mevcut durum, hedef durum ve teknik riskler birlikte değerlendirilir.
  • REST, veri erişimi, portallar ve Rollout daha sonra ortaya çıkan sonuçlar olarak ertelenmez.
  • Hangi yolun ekonomik ve işletme açısından sürdürülebilir olduğunu erken görürsünüz.

Gönderiyi paylaş

Bu gönderiyi doğrudan paylaş

LinkedIn, X, XING, Facebook, WhatsApp ve e-posta hemen kullanılabilir. Instagram için bağlantıyı ve kısa metni doğrudan hazırlıyoruz.

E-posta

Instagram yeni bir sekmede açılır. Bağlantı ve kısa metin önceden panoya kopyalanır.