Autofac Создание дочерней области для нового потока не работает должным образом - PullRequest
0 голосов
/ 19 апреля 2019

Когда кто-то вызывает URL «/ index / 5», я хочу вернуть представление, и параллельно я хочу начать новый поток, в котором я хочу выяснить с некоторыми вызовами БД и бизнес-логикой, должен ли я отправлятькто-то еще уведомление.Вот упрощенное представление моей установки, которое выдает ошибку.

Как я могу заставить свою дочернюю область работать в параллельном потоке?

Запуск приложения

var builder = new ContainerBuilder();
builder.RegisterType<MyRepository>().As<IMyRepository();
builder.RegisterType<Entities>().As<Entities>().InstancePerRequest();
var container = builder.Build();

Контроллер

    private readonly IMyRepository _myRepository ;

    public MyController(
        IMyRepository myRepository
       )
    {
        _myRepository = myRepository;
    }

    public async Task<ActionResult> Index(int id)
    {
        _myRepository.DoSomething(id);

        return View();
    }

Репозиторий:

private ILifetimeScope _lifeTimeScopeChild = null;

public void DoSomething(int id){
            //start new thread with child scope
            using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
            {
                _lifeTimeScopeChild = threadLifeTime;
                 Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
                 t.Start(id);  
            }
        }

        private void MySeparateThread(object id) {
                    var _entities = _lifeTimeScopeChild.Resolve<Entities>(); //IT CRASHES HERE
        }

Ошибка:

Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.

Что я пытаюсь сделать: https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#thread-scope

1 Ответ

3 голосов
/ 19 апреля 2019

Ключевой частью этого является не первая часть сообщения об ошибке, а последняя часть:

уже утилизировано.

using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
{
    _lifeTimeScopeChild = threadLifeTime;
    Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
    t.Start(id);  
}

threadLifeTime создается в объявлении вашего блока using. В конце этого блока он утилизируется. Это единственная цель блока using. В нем заложено предположение, что в этот момент можно расположить объект. Вы закончили с этим.

Вы также создаете отдельный поток и передаете ему threadLifeTime. Часть кода, где это происходит, не показана, но это то, что происходит. Возможно, вы не передаете его явно, но на него ссылаются где-то в ParameterizedThreadStart и MySeparateThread.

Этот поток продолжает выполняться отдельно от метода, который первоначально вызвал его. Так что сразу после того, как вы сдадите этот предмет, он утилизируется Что бы ни делал этот поток, он пытается сделать это с удаленным объектом. Это ошибка.

Обычно в конце блока using переменная (threadLifeTime) выходит из области видимости. Там не будет никаких ссылок на него, поэтому не имеет значения, если он будет утилизирован. Но теперь есть ссылка на это, что является проблемой, поскольку это ссылка на что-то, что было уничтожено.

Краткосрочное решение - не создавать отдельную ветку. Если есть правильный ответ на этот вопрос, который включает в себя многопоточность, это более сложно и выходит за рамки этого ответа. Хорошее практическое правило - сначала заставить код работать без многопоточности, а затем добавить его, если вам это нужно. В противном случае вы не знаете, является ли проблема многопоточностью или чем-то еще.

Другая проблема заключается в следующем:

_lifeTimeScopeChild = threadLifeTime;

Это указывает, что он не только передается в какой-то другой поток, но также присваивается полю внутри класса. Это тоже проблема по той же причине. Ссылка, которую вы назначаете этому полю, будет существовать даже после удаления объекта. Если что-то попытается использовать _lifeTimeScopeChild после того, как этот блок using завершится, он получит ту же ошибку.

Вопрос, на который нужно ответить: «Нужен ли этот объект моему методу или моему классу нужен этот объект?»

Если ваш метод нуждается в этом, объявите и используйте его в методе, но не позволяйте ссылкам на него, которые вы не можете контролировать, «убежать» из метода. Если вы избавитесь от этого, то сломаете все, что пытается его использовать. И если вы не избавитесь от него, тогда, ну, он не избавится, когда должен.

Если вашему классу это нужно, подумайте о том, чтобы создать его при создании класса или использовать ленивый экземпляр для его создания, когда он вам нужен. (Я бы начал с первого.) Затем заставьте ваш класс реализовать IDisposable, и когда ваш класс будет утилизирован, тогда вы избавитесь от любых доступных ресурсов, используемых вашим классом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...