Parallele Aufgaben und Hintergrundkontext | Optimizely Developer Commu

Wir haben kürzlich (CMS.Core 12.16.0) eine Änderung vorgenommen, um asynchrone asynchrone/wartende Ausführungsabläufe sowie die Isolierung der Dienstauflösung vom IOC-Container in parallelen Kontexten besser zu unterstützen.

Diese Änderung kann sich auf Code auswirken, der mehrere parallele Aufgaben erzeugt. Bei solchem ​​Code können Fehler wie „Befehl kann ohne bestehende Verbindung nicht erstellt werden“ oder „Die Verbindung ist geschlossen“ auftreten. Der Grund dafür ist, dass zur Unterstützung der asynchronen asynchronen/wartenden Ausführung der Datenbankkontext zusammen mit dem anderen Ausführungskontext „geflossen“ ist. Aber bei asynchronen/wartenden Flüssen bleibt der Ausführungskontext beispielsweise auch erhalten Task.Run oder Task.Factory.StartNew Anrufe. Dies hat zur Folge, dass bei der parallelen Erstellung neuer Aufgaben mehrere gleichzeitige Aufgaben denselben Datenbankkontext verwenden, was zu Problemen führen kann, da der Datenbankkontext nicht threadsicher ist. Dies kann beispielsweise zu den zuvor genannten Ausnahmen führen.

Hintergrundkontext

Um die Szenarien zu unterstützen, in denen parallel neue Aufgaben/Threads entstehen, haben wir einen Dienst eingeführt IBackgroundContextFactory (Es wurde in CMS.Core.12.16.0 eingeführt, aber es gibt einen Fehler, der in CMS.Core.12.17.1 behoben wurde, sodass diese Version oder höher empfohlen wird). Es gibt eine einzige Methode wie:

    /// 
    /// Used to create a new context that has its own scope for services and request caches.
    /// Recommended to use for background task/threads
    /// 
    public interface IBackgroundContextFactory
    {
        /// 
        /// Creates a new context that has its own scope for services and request caches.
        /// 
        /// A disposable context.
        IBackgroundContext Create();
    }

Der erstellte Kontext enthält einen isolierten Kontext für die Ausführung, was bedeutet, dass er einen eigenen Gültigkeitsbereich hat IServiceProvider (der verworfen wird, wenn der Kontext verworfen wird) und ein isolierter Ausführungskontext (einschließlich Datenbankkontext). Die Ausführung in diesem Hintergrundkontext unterstützt auch die asynchrone asynchrone/wartende Ausführung.

Lesen Sie auch  Wie Troyes und Aube olympische und paralympische Delegationen anziehen

Der bereichsbezogene Dienstanbieter stellt außerdem sicher, dass die Dienste so lange wie der Kontext erhalten bleiben. Dies schützt vor Dingen wie ObjectDisposedException, die Sie zuvor erhalten könnten, wenn Sie eine parallele Aufgabe innerhalb einer HTTP-Anfrage erstellt hätten (z. B. einen Ereignishandler für beispielsweise IContentEvents, der eine Hintergrundaufgabe startet). Der Grund für die ObjectDisposedException besteht darin, dass die Aufgabe den bereichsbezogenen Container aus der http-Anfrage verwendet. Wenn die http-Anfrage dann vor der Hintergrundaufgabe endet, erhalten Sie möglicherweise eine ObjectDisposedException.

Empfohlene Verwendung

Wenn Sie Code haben, der neue Aufgaben/Threads hervorbringt (z. B Task.Run, Task.Factory.StartNew, Parallel.ForEach Wenn der in der Aufgabe auszuführende Code auf andere Dienste zugreift, empfiehlt es sich, einen Hintergrundkontext für den Thread zu erstellen. Angenommen, Sie haben Code wie:

        var tasks = new List();
        foreach (var thing in things)
        {
            tasks.Add(Task.Run(() =>
            {
	           //Some code that run in parallel
            }));
        }

        Task.WaitAll(tasks.ToArray());

Dann wäre der Vorschlag, es zu ändern:

        var tasks = new List();
        foreach (var thing in things)
        {
            tasks.Add(Task.Run(() =>
            {
                using (var backgroundContext = _backgroundContextFactory.Create())
                //below is example on how to get a scoped service
                //ServiceLocator.Current and Injected<> also works with background context (even if usage of those are not recommended as best practice)
                var isolatedScopedService = backgroundContext.Service.Get();
                //Some code that run in parallel
            }));
        }

        Task.WaitAll(tasks.ToArray());

Die Empfehlung lautet daher, dass die erste Anweisung innerhalb der Aufgabe/des Threads darin bestehen sollte, einen Hintergrundkontext für die Ausführung der Aufgabe zu erstellen.

Notiz dass Sie keinen neuen Hintergrundkontext innerhalb eines „normalen“ asynchronen/wartenden Ausführungsablaufs erstellen müssen (und sollten) (das heißt, wenn Sie auf die Aufgaben warten und sie nicht parallel ausführen). Denn in diesem Fall wird nur ein Thread gleichzeitig ausgeführt, und in diesem Fall möchten Sie, dass der Ausführungskontext (einschließlich Datenbankkontext) zwischen den potenziell verschiedenen Threads fließt, die Teil des asynchronen asynchronen/wartenden Ausführungsablaufs sind.

31. August 2023

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.