Как правильно сделать многопоточность в Azure - PullRequest
4 голосов
/ 12 апреля 2011

У меня есть приложение 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.
        }
    }
}

Ответы [ 4 ]

3 голосов
/ 12 апреля 2011

Я думаю, что это довольно распространенный сценарий - в идеальном мире одна работа на роль, но по денежным соображениям (и экологическим причинам!) Лучше объединить их в одну роль.

Лично я не думаю, что есть ПРАВИЛЬНЫЙ способ - есть несколько доступных вариантов, и вам нужно выбрать тот, который наилучшим образом соответствует текущим требованиям вашего приложения:

  • используйтеБиблиотека обработки задач
  • использует Quartz.net для запланированных задач - http://quartznet.sourceforge.net/
  • Lokad.Cloud предоставляет несколько платформ для обслуживания нескольких очередей и для выполнения регулярных запланированных задач - http://lokadcloud.codeplex.com/
  • сворачивайся
1 голос
/ 12 апреля 2011

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

0 голосов
/ 12 апреля 2011

Я создам поток для каждой из этих задач с длительным сроком службы.

На самом деле это то, что я делаю в одной из моих ролей: Запуск нескольких рабочих ролей на экземпляр

Я загружаю типы и вызываю метод Run в отдельном потоке.

0 голосов
/ 12 апреля 2011

Сделайте каждую задачу отдельным .exe и удалите их из своего класса WorkerRole. Оттуда следите за процессами на предмет неожиданного завершения (Process.WaitForExit ()) и запускайте их снова. (Но подумайте о том, что произойдет, если один процесс будет постоянно падать - он будет использовать 100% процессорный запуск, сбой и повторный запуск. Возможно, для этого используйте экспоненциальный откат.)

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

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