Home » Параллельные задачи и BackgroundContext | Optimizely Developer Commu

Параллельные задачи и BackgroundContext | Optimizely Developer Commu

Недавно мы (CMS.Core 12.16.0) внесли изменения для лучшей поддержки асинхронных потоков выполнения async/await, а также изоляции разрешения службы от контейнера IOC в параллельных контекстах.

Это изменение может повлиять на код, который порождает несколько параллельных задач. В таком коде могут возникнуть ошибки типа «Невозможно создать команду без существующего соединения» или «Соединение закрыто». Причина этого в том, что для поддержки асинхронного выполнения async/await контекст базы данных «объединяется» вместе с другим контекстом выполнения. Но что касается потоков async/await, контекст выполнения также сохраняется, например, Задача.Выполнить или Task.Factory.StartNew звонки. Это приводит к тому, что если новые задачи создаются параллельно, несколько одновременных задач будут использовать один и тот же контекст базы данных, что может вызвать проблемы, поскольку контекст базы данных не является потокобезопасным. Это может, например, привести к исключениям, упомянутым ранее.

ФонКонтекст

Для поддержки сценариев, в которых новые задачи/потоки создаются параллельно, мы ввели сервис IBackgroundContextFactory (он был представлен в CMS.Core.12.16.0, но есть ошибка, которая была устранена в CMS.Core.12.17.1, поэтому рекомендуется использовать эту версию или более позднюю). Он имеет один метод, например:

    /// 
    /// 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();
    }

Созданный контекст будет содержать изолированный контекст для выполнения, что означает, что он будет иметь собственную область действия. IServiceProvider (который будет удален при удалении контекста) и изолированный контекст выполнения (включая контекст базы данных). Выполнение в этом фоновом контексте также поддерживает асинхронное выполнение async/await.

Read more:  Параметры кэша вывода в Optimizely CMS 12

Поставщик услуг с заданной областью также обеспечит сохранение услуг до тех пор, пока контекст. Это защищает от таких вещей, как ObjectDisposeException, которые вы могли получить до этого, если создали параллельную задачу в HTTP-запросе (например, обработчик событий, например, IContentEvents, который запускает фоновую задачу). Причина ObjectDisposeException заключается в том, что задача будет использовать контейнер с заданной областью из http-запроса, а затем, если http-запрос завершится до фоновой задачи, вы можете получить ObjectDisposeException.

Рекомендуемое использование

Если у вас есть код, который порождает новые задачи/потоки (например, Задача.Выполнить, Task.Factory.StartNew, Параллельный.ForEach или неожиданные задачи), то если код, который будет выполняться в задаче, обращается к другим сервисам, то рекомендуется создать фоновый контекст для потока. Итак, скажем, например, у вас есть такой код:

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

        Task.WaitAll(tasks.ToArray());

Тогда было бы предложено изменить его на:

        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());

Поэтому рекомендуется, чтобы первый оператор внутри задачи/потока создавал фоновый контекст для выполнения задачи.

Примечание что вам не нужно (и не следует) создавать новый фоновый контекст в «обычном» потоке выполнения async/await (то есть, когда вы ожидаете задач, а не выполняете их параллельно). Потому что в этом случае одновременно выполняется только один поток, и в этом случае вы хотите, чтобы контекст выполнения (включая контекст базы данных) перемещался между потенциально разными потоками, которые являются частью асинхронного потока выполнения async/await.

31 августа 2023 г.

Leave a Comment

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