/* Page prose (de) */
"HardToCatch" = "# Warum es so schwer zu fassen ist\n\nDie Beschädigung hängt vom *Timing* ab: welcher Kassierer zufällig während der Lücke eines anderen liest. Das Timing verschiebt sich mit der CPU-Last, der Anzahl der Kerne, damit, was das Gerät sonst noch tut, und mit purem Glück. Genau das bedeutet **nichtdeterministisch**: derselbe Code, dieselben Eingaben, unterschiedliche Ergebnisse. Also der Fehler:\n\n* **Lässt sich vielleicht nicht reproduzieren.** Er hat deine Tests bestanden, weil deine Tests Glück hatten.\n* **Verschwindet vielleicht unter einem Debugger,** der das Timing verändert.\n* **Beißt vielleicht nur in der Produktion,** auf einem stärker ausgelasteten oder langsameren Gerät als deinem.\n\nDu kannst dich nicht darauf verlassen, ihn durch Ausführen zu finden. Du findest ihn, indem du danach *liest*. Die eine Frage, die zählt:\n\n**Gibt es Zustand, der zwischen nebenläufigen Aufgaben geteilt und mutiert wird, ohne dass irgendetwas den Zugriff koordiniert?**\n\nIm Beispiel: `account.balance` wird geteilt (ein `Account`, von jeder Closure eingefangen), er wird mutiert (`account.balance = updated`), und nichts koordiniert die nebenläufige `tellers`-Warteschlange. Drei von drei: das ist ein Data Race, auf dem Papier beweisbar, ohne dass ein Testlauf nötig ist.\n\n* callout(Am Rande): In einem vollständigen Xcode-Projekt gibt es ein Werkzeug namens **Thread Sanitizer**, das Data Races zur Laufzeit meldet. Es gibt es nur in Xcode, du wirst es in Swift Playground nicht haben, also lernst du in diesem ganzen Buch, Races von Hand herzuleiten, was ohnehin die dauerhaftere Fähigkeit ist.\n\n[Zurück](@previous)  |  Seite 3 von 9  |  [Weiter: Die Lösung mit serieller Warteschlange](@next)";
