{"id":568,"date":"2026-10-01T19:13:28","date_gmt":"2026-10-01T19:13:28","guid":{"rendered":"https:\/\/www.donaldswelt.ch\/?p=568"},"modified":"2026-10-01T20:01:35","modified_gmt":"2026-10-01T20:01:35","slug":"niemand-versteht-die-logik-sie-gilt-daher-als-stabil","status":"publish","type":"post","link":"https:\/\/www.donaldswelt.ch\/index.php\/2026\/10\/01\/niemand-versteht-die-logik-sie-gilt-daher-als-stabil\/","title":{"rendered":"Niemand versteht die Logik. Sie gilt daher als stabil."},"content":{"rendered":"<div class=\"fusion-fullwidth fullwidth-box fusion-builder-row-1 fusion-flex-container nonhundred-percent-fullwidth non-hundred-percent-height-scrolling\" style=\"--awb-border-radius-top-left:0px;--awb-border-radius-top-right:0px;--awb-border-radius-bottom-right:0px;--awb-border-radius-bottom-left:0px;--awb-flex-wrap:wrap;\" ><div class=\"fusion-builder-row fusion-row fusion-flex-align-items-flex-start fusion-flex-content-wrap\" style=\"max-width:1248px;margin-left: calc(-4% \/ 2 );margin-right: calc(-4% \/ 2 );\"><div class=\"fusion-layout-column fusion_builder_column fusion-builder-column-0 fusion_builder_column_1_1 1_1 fusion-flex-column\" style=\"--awb-bg-size:cover;--awb-width-large:100%;--awb-margin-top-large:0px;--awb-spacing-right-large:1.92%;--awb-margin-bottom-large:0px;--awb-spacing-left-large:1.92%;--awb-width-medium:100%;--awb-spacing-right-medium:1.92%;--awb-spacing-left-medium:1.92%;--awb-width-small:100%;--awb-spacing-right-small:1.92%;--awb-spacing-left-small:1.92%;\"><div class=\"fusion-column-wrapper fusion-flex-justify-content-flex-start fusion-content-layout-column\"><div class=\"fusion-text fusion-text-1\"><p><strong>Die Geschichte dahinter<\/strong><\/p>\n<p>Manchmal lernt man in .NET etwas Neues kennen und fragt sich danach vor allem, wie man es all die Jahre \u00fcbersehen konnte. Heute war das bei mir der <a href=\"https:\/\/learn.microsoft.com\/de-de\/dotnet\/api\/system.threading.periodictimer?view=net-10.0\" target=\"_blank\" rel=\"noopener\"><span style=\"color: #0000ff;\"><code dir=\"ltr\">PeriodicTimer<\/code><\/span><\/a><\/p>\n<p>Timer sind in .NET nun wirklich nichts Neues. Mit <span style=\"color: #0000ff;\"><code dir=\"ltr\">System.Threading.Timer<\/code><\/span>, <span style=\"color: #0000ff;\"><code dir=\"ltr\">System.Timers.Timer<\/code><\/span> und dem guten alten <span style=\"color: #0000ff;\"><code dir=\"ltr\">System.Windows.Forms.Timer<\/code><\/span> gibt es bereits eine erstaunliche Auswahl an M\u00f6glichkeiten, Dinge regelm\u00e4ssig passieren zu lassen. Und trotzdem stand pl\u00f6tzlich noch einer vor der T\u00fcr: <span style=\"color: #0000ff;\"><code dir=\"ltr\">PeriodicTimer<\/code><\/span>.<\/p>\n<p>Eigentlich eine angenehm moderne Variante. Kein klassischer Callback, kein Event, sondern etwas, das sich sehr sch\u00f6n in die Welt von <span style=\"color: #0000ff;\"><code dir=\"ltr\">async<\/code><\/span> und <span style=\"color: #0000ff;\"><code dir=\"ltr\">await<\/code><\/span> einf\u00fcgt. Damit h\u00e4tte die Geschichte auch schon wieder vorbei sein k\u00f6nnen. Ich h\u00e4tte sagen k\u00f6nnen: <strong>Wieder etwas gelernt.<\/strong> Und anschliessend etwas Sinnvolles tun.<\/p>\n<p>Das w\u00e4re vern\u00fcnftig gewesen.<\/p>\n<p>Stattdessen entstand die Frage, was eigentlich passiert, wenn man die verschiedenen Generationen von .NET-Zeitmessung miteinander bekannt macht. Wenn wir schon einen modernen <span style=\"color: #0000ff;\"><code dir=\"ltr\">PeriodicTimer<\/code><\/span> haben, k\u00f6nnte man schliesslich auch noch einen klassischen Timer dazunehmen. Und wenn wir bereits dabei sind, verschiedene Epochen der Softwareentwicklung miteinander zu vermischen, darf nat\u00fcrlich ein alter Bekannter nicht fehlen: <span style=\"color: #0000ff;\"><code dir=\"ltr\">goto<\/code><\/span>.<\/p>\n<p>Nicht weil man es braucht, sondern weil es da ist.<\/p>\n<p>Damit war aus einem kleinen Experiment langsam etwas geworden, das man mit etwas Wohlwollen als Zustandsmaschine bezeichnen k\u00f6nnte. Mit weniger Wohlwollen wahrscheinlich eher als Unfall.<\/p>\n<p>Irgendeine Logik musste das Ganze nat\u00fcrlich trotzdem bekommen. Und genau an dieser Stelle kam ein v\u00f6llig anderes Thema ins Spiel, das mit Timern eigentlich \u00fcberhaupt nichts zu tun hat: das <strong>Doomsday-Ph\u00e4nomen<\/strong>.<\/p>\n<p>Bestimmte leicht merkbare Daten eines Jahres fallen immer auf denselben Wochentag. Dazu geh\u00f6ren unter anderem <strong>4.4., 6.6., 8.8., 10.10. und 12.12.<\/strong> Das ist mathematisch durchaus interessant. F\u00fcr einen Heartbeat-Mechanismus ist es dagegen vollkommen unn\u00f6tig.<\/p>\n<p>Also selbstverst\u00e4ndlich perfekt geeignet.<\/p>\n<p>Ein Zufallsgenerator durfte nun eines dieser Daten ausw\u00e4hlen, anschliessend wurde der Wochentag verglichen und daraus der Startwert f\u00fcr den Heartbeat bestimmt. Das Ergebnis dieser beeindruckenden Konstruktion ist zuverl\u00e4ssig: <strong>1<\/strong><\/p>\n<p>Man k\u00f6nnte diesen Wert nat\u00fcrlich auch direkt hinschreiben. Aber wo bliebe dann die Wissenschaft?<\/p>\n<p>Damit waren irgendwann moderne asynchrone Programmierung, klassische Timer, ein <span style=\"color: #0000ff;\"><code dir=\"ltr\">goto<\/code><\/span> aus vergangenen Zeiten und ein kalendarisches Ph\u00e4nomen miteinander verbunden. Eigentlich Dinge, die sich vermutlich nie kennenlernen sollten.<\/p>\n<p>Das \u00dcberraschende daran war nicht, dass der Code kompliziert aussah. Das \u00dcberraschende war:<\/p>\n<p><strong>Er funktionierte.<\/strong><\/p>\n<p>Im Log lief der Ablauf sauber Sekunde f\u00fcr Sekunde durch. <span style=\"color: #0000ff;\"><code dir=\"ltr\">Delay<\/code><\/span>, <span style=\"color: #0000ff;\"><code dir=\"ltr\">Periodic<\/code><\/span>, Callback, noch ein Callback, wieder <span style=\"color: #0000ff;\"><code dir=\"ltr\">Periodic<\/code><\/span>, noch ein Callback und anschliessend zur\u00fcck zum Anfang. Fast wie ein Schweizer Uhrwerk.<\/p>\n<p>Nur dass ein Schweizer Uhrmacher vermutlich sp\u00e4testens beim ersten <span style=\"color: #0000ff;\"><code dir=\"ltr\">goto<\/code><\/span> seine Werkzeugkiste genommen und den Raum verlassen h\u00e4tte.<\/p>\n<p><strong>Fazit<\/strong><\/p>\n<blockquote>\n<p><strong>Niemand versteht die Logik. Sie gilt daher als stabil.<\/strong><\/p>\n<\/blockquote>\n<p>Aus einem harmlosen <span style=\"color: #0000ff;\"><code dir=\"ltr\">PeriodicTimer<\/code><\/span> wurde ein Ausflug durch mehrere Generationen von .NET, etwas <span style=\"color: #0000ff;\"><code dir=\"ltr\">goto<\/code><\/span>-Nostalgie und ein v\u00f6llig unn\u00f6tig eingebautes Doomsday-Ph\u00e4nomen. <strong>Etwas Neues gelernt, etwas Altes wieder ausgegraben und dabei erstaunlich viel Spass gehabt.<\/strong><\/p>\n<p><strong>Der Code dazu<\/strong><\/p>\n<p>Wer sich den kompletten speziellen Heartbeat-Mechanismus ansehen m\u00f6chte, findet hier den vollst\u00e4ndigen Code:<\/p>\n<\/div><style type=\"text\/css\" scopped=\"scopped\">.fusion-syntax-highlighter-1 > .CodeMirror, .fusion-syntax-highlighter-1 > .CodeMirror .CodeMirror-gutters {background-color:var(--awb-color7);}.fusion-syntax-highlighter-1 > .CodeMirror .CodeMirror-gutters { background-color: var(--awb-color2); }.fusion-syntax-highlighter-1 > .CodeMirror .CodeMirror-linenumber { color: var(--awb-color8); }<\/style><div class=\"fusion-syntax-highlighter-container fusion-syntax-highlighter-1 fusion-syntax-highlighter-theme-dark\" style=\"opacity:0;margin-top:0px;margin-right:0px;margin-bottom:0px;margin-left:0px;font-size:14px;border-width:1px;border-style:solid;border-color:var(--awb-color3);\"><div class=\"syntax-highlighter-copy-code\"><span class=\"syntax-highlighter-copy-code-title\" data-id=\"fusion_syntax_highlighter_1\" style=\"font-size:14px;\">Copy to Clipboard<\/span><\/div><label for=\"fusion_syntax_highlighter_1\" class=\"screen-reader-text\">Syntax Highlighter<\/label><textarea class=\"fusion-syntax-highlighter-textarea\" id=\"fusion_syntax_highlighter_1\" data-readOnly=\"nocursor\" data-lineNumbers=\"1\" data-lineWrapping=\"\" data-theme=\"oceanic-next\" data-mode=\"text\/javascript\">    var random = new Random();\n\t\n  \/\/ Spezieller Heartbeat-Mechanismus by D. Peter :-)\n  \/\/ Niemand versteht die Logik. Sie gilt daher als stabil.\n  Delay:\n\n    await Task.Delay(1000);\n\n    _logger.Debug(\"Timer[Delay]: Second={Second}\", DateTime.Now.Second);\n\n    int v = new int[] { 6, 8, 10, 12 }[random.Next(4)];\n\n    var heartbeat =\n      new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 4, 4, 0).DayOfWeek\n      == new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, v, v, 0).DayOfWeek ? 1 : 0;\n\n  Timer:\n\n    var timer = new PeriodicTimer(TimeSpan.FromSeconds(1));\n\n    while(await timer.WaitForNextTickAsync())\n    {\n      _logger.Debug(\"Timer[Periodic]: Second={Second}\", DateTime.Now.Second);\n\n    Callback:\n      \n      var tcs = new TaskCompletionSource<bool>();\n\n      new Timer(new((state) =>\n        {\n          _logger.Debug(\"Timer[Callback][{Heartbeat}]: Second={Second}\", heartbeat, DateTime.Now.Second);\n\n          switch(heartbeat)\n          {\n            case 1: tcs.TrySetResult(true); break;\n            case 2: tcs.TrySetResult(false); break;\n            default: tcs.TrySetCanceled(); break;\n          }\n        }), null, 1000, Timeout.Infinite);\n      \n      try\n      {\n        await tcs.Task;\n      }\n      catch(OperationCanceledException)\n      {\n        \/\/ Ignorieren, da dies Teil des Heartbeat-Mechanismus ist.\n      }\n\n      heartbeat++;\n\n      if(tcs.Task.IsCompletedSuccessfully)\n      {\n        if(tcs.Task.Result)\n          goto Callback;\n\n        goto Timer;\n      }\n      else\n      {\n        timer.Dispose();\n        goto Delay;\n      }\n    }<\/textarea><\/div><div class=\"fusion-text fusion-text-2\"><pre>Ausgabe:\r\n[20:31:14 DBG] Timer[Delay]: Second=14\r\n[20:31:15 DBG] Timer[Periodic]: Second=15\r\n[20:31:16 DBG] Timer[Callback][1]: Second=16\r\n[20:31:17 DBG] Timer[Callback][2]: Second=17\r\n[20:31:18 DBG] Timer[Periodic]: Second=18\r\n[20:31:19 DBG] Timer[Callback][3]: Second=19\r\n[20:31:20 DBG] Timer[Delay]: Second=20\r\n[20:31:21 DBG] Timer[Periodic]: Second=21\r\n[20:31:22 DBG] Timer[Callback][1]: Second=22\r\n[20:31:23 DBG] Timer[Callback][2]: Second=23\r\n[20:31:24 DBG] Timer[Periodic]: Second=24\r\n[20:31:25 DBG] Timer[Callback][3]: Second=25\r\n[20:31:26 DBG] Timer[Delay]: Second=26\r\n[20:31:27 DBG] Timer[Periodic]: Second=27\r\n[20:31:28 DBG] Timer[Callback][1]: Second=28\r\n[20:31:29 DBG] Timer[Callback][2]: Second=29\r\n[20:31:30 DBG] Timer[Periodic]: Second=30\r\n[20:31:31 DBG] Timer[Callback][3]: Second=31\r\n[20:31:33 DBG] Timer[Delay]: Second=33\r\n[20:31:34 DBG] Timer[Periodic]: Second=34<\/pre>\n<\/div><\/div><\/div><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":570,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[13,18],"tags":[],"class_list":["post-568","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-comics","category-technikwelt"],"_links":{"self":[{"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/posts\/568","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/comments?post=568"}],"version-history":[{"count":16,"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/posts\/568\/revisions"}],"predecessor-version":[{"id":591,"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/posts\/568\/revisions\/591"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/media\/570"}],"wp:attachment":[{"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/media?parent=568"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/categories?post=568"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.donaldswelt.ch\/index.php\/wp-json\/wp\/v2\/tags?post=568"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}