Я хочу задать вопрос, который приходит мне в голову. Этот вопрос о доступе к памяти содержит объект с временем жизни singleton в ядре asp.net. Итак, предположим, что в этой структуре существует два потока. один из них - обычный поток запроса / ответа, используемый в сети asp. Другой поток - это непрерывно работающая рабочая служба в фоновом режиме.

Мой план - создать очередь задач. И в очереди я храню задачи, которые не хочу выполнять в потоке запроса / ответа. Эти сохраненные функции постоянно выполняются в фоновом режиме.

Этот раздел кода находится в очереди задач. Итак, этот класс используется в фоновой службе рабочего и где угодно на asp.net.

public class EventQueue : IEventQueue
{
            public LinkedList<Task> Queue = new LinkedList<Task>();
    
            public void AddEvent(Task task)
            {
                Queue.AddFirst(task);
            }
    
            public Task GetNextEvent()
            {
                var task = Queue.Last.Value;
                Queue.RemoveLast();
                return task;
            }
    }

Этот раздел кода содержит рабочую службу. Он выполняет одно за другим выполнение в задачах очереди

public class QueueWorker : BackgroundService
    {
        private readonly IEventQueue _queue;

        public QueueWorker(IEventQueue queue)
        {
            _queue = queue;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {         
                    var task = _queue.GetNextEvent();
                    if (task != null)
                        task.RunSynchronously();                
            }
        }
    }

Этот кодовый раздел содержит зарегистрированные службы.

services.AddSingleton<IEventQueue,EventQueue>();
services.AddHostedService<QueueWorker>();

Вопросов:

  • Эта структура хорошо работает? Я думаю, это не сработает. Bcs есть множественный доступ к экземпляру очереди. Или, скорее, рабочая служба всегда обращается к экземпляру очереди. Следовательно, не будет времени для доступа к другим потокам. Так этот подход правильный?
  • Если бы время жизни синглтона не использовалось, а EventQueue был статическим (по крайней мере, свойство LinkedList было статическим), все было бы по-другому?
  • Есть ли у вас предложения по улучшению этой конструкции?
0
ikbalkazanc 15 Окт 2021 в 20:09

2 ответа

Лучший ответ

Я посоветую не обрабатывать задачи в отдельном потоке очереди с каким-то рабочим фоном.

Вместо этого рассмотрите возможность использования async Tasks, .net действительно хорош для проталкивания данных, поскольку каждая задача имеет собственный поток, в любом случае эта структура достаточно освобождает ваш поток ответа:

[HttpGet("")]
public async Task<IActionResult> Get(){
  return await Task<IActionResult>.Factory.StartNew(() => {
      //TODO: Your lengthy task here
  });
}

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

private readonly IWorkerInterface _worker;

public ControllerConstructor(IWorkerInterface worker){
  //Assign variable to controller variable
  _worker = worker ?? throw new ArgumentNullException(nameof(worker));
}

[HttpGet("")]
public async Task<IActionResult> Get(){
  return await Task<IActionResult>.Factory.StartNew(() => {
      //TODO: Your lengthy task here
      _worker.DoWork();
  });
}

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

Если вам нужна потокобезопасная очередь, подумайте об использовании ConcurrentQueue

Проблема с фоновым рабочим, поскольку я вижу, что это не пул потоков, если он не нужен, а пул потоков уже находится в вашем распоряжении через task.factory

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

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

-1
T. Nielsen 15 Окт 2021 в 17:43

Для такой задачи я бы посоветовал взглянуть на Коллекция ConcurrentQueue.

Это должно обеспечить потокобезопасную коллекцию без каких-либо блокировок. В качестве альтернативы вы можете использовать объект SemaphoreSlim в качестве блокировки, если вы не можете использовать вышеупомянутую очередь.

Насколько мне известно, использование ConcurrentQueue должно удалить упомянутый вами блок доступа. Если вы выберете SemaphoreSlim, я бы предложил небольшой Task.Delay () в цикле фонового обслуживания.

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

0
quain 15 Окт 2021 в 17:31