У меня есть приложение Azure, для которого требуются 3 совершенно уникальных задачи, выполняемых как длительные процессы. Я мог бы легко раскрутить 3 рабочие роли, но это стоило бы в 3 раза больше, чем мне нужно было бы потратить, учитывая, насколько просты задачи. Я хотел бы запускать каждую задачу в отдельном потоке, поскольку каждая задача сильно отличается по времени, которое требуется для ее выполнения, и частоте необходимости ее запуска.
Учитывая этот сценарий или даже общий многопоточный сценарий Azure, каков наилучший способ выполнения каждой задачи независимо друг от друга?
Несколько соображений, которые я хотел бы предложить:
- Сохраняйте низкую загрузку ЦП (не всегда возможно с EventWaitHandles ..?).
- Используются самые последние фреймворк-утилиты (т. Е. TPL или PLINQ).
- Изящное завершение работы и перезапуск в случае неисправимого исключения.
Спасибо за предложения, ссылки или примеры кода.
EDIT
Вот то, на чем я остановился - учитывая, что код привязан к облаку Azure, вариант самовосстановления кажется наиболее подходящим. Этот подход нацелен на сбор данных и делегирование их работы различным «единицам работы».
Вот как это склеено. Во-первых, простой класс, чтобы обернуть разнообразие работников.
public class ThreadWorker
{
internal void RunInternal()
{
try
{
Run();
}
catch (SystemException)
{
throw;
}
catch (Exception)
{
}
}
public virtual void Run()
{
}
public virtual void OnStop()
{
}
}
Далее код, позволяющий бесконечно долго поддерживать серию потоков. Это включает в себя список потоков, список «рабочих» (по одному на поток) и бесконечный цикл, который проверяет состояние каждого потока в заданном интервале.
Обратите внимание, что исключение, которое завершает поток, не разрушит другие потоки. Если возникнет необработанное исключение, поток будет перезапущен при следующем ответе на обработчик события.
private readonly List<Thread> _threads = new List<Thread>();
private readonly List<ThreadWorker> _workers = new List<ThreadWorker>();
private EventWaitHandle EventWaitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
public override void Run()
{
foreach (var worker in _workers)
{
_threads.Add(new Thread(worker.RunInternal));
}
foreach (var thread in _threads)
{
thread.Start();
}
while (!EventWaitHandle.WaitOne(500))
{
// Restart any threads that have stopped running
for (var i = 0; i < _threads.Count; i++)
{
if (_threads[i].IsAlive)
{
continue;
}
_threads[i] = new Thread(_workers[i].RunInternal);
_threads[i].Start();
}
EventWaitHandle.WaitOne(2000);
}
}
Достаточно просто - основной поток (в данном случае метод Run рабочей роли Azure) блокируется на полсекунды между проверками активного списка потоков. Прекращенные потоки перезапускаются, и затем главный поток блокируется на 2 секунды, а затем снова проверяет состояние рабочих потоков.
Добавление рабочих так же просто, как добавление списка классов, которые наследуются от базового класса ThreadWorker
_workers.Add(new SomeWorker());
_workers.Add(new AnotherWorker());
И, наконец, бетонщики:
public class SomeWorker: ThreadWorker
{
public override void Run()
{
while (true)
{
// Do your long running work here.
Thread.Sleep(2000); // Take a breather - not necessary.
}
}
}
public class AnotherWorker: ThreadWorker
{
public override void Run()
{
while (true)
{
// Do your long running work here.
}
}
}