структуры данных для планирования рабочего процесса?

Мне интересно, какие структуры данных/алгоритмы могут помочь облегчить обработку следующей ситуации; Я не уверен, нужен ли мне один FIFO, приоритетная очередь или несколько FIFO.

У меня есть N объектов, которые должны пройти через предопределенный рабочий процесс. Каждый объект должен выполнить шаг 1, затем шаг 2, затем шаг 3, затем шаг 4 и т. д. Каждый шаг либо выполняется быстро, либо включает «ожидание», которое зависит от чего-то внешнего для завершения (например, завершения операции с файлом или чего-то еще). ). Каждый объект поддерживает свое собственное состояние. Если бы мне пришлось определить интерфейс для этих объектов, это было бы примерно так (написано ниже на псевдо-Java, но этот вопрос не зависит от языка):

public interface TaskObject
{
   public enum State { READY, WAITING, DONE };
   // READY = ready to execute next step
   // WAITING = awaiting some external condition
   // DONE = finished all steps

   public int getCurrentStep();
   // returns # of current step

   public int getEndStep();
   // returns # of step which is the DONE case.

   public State getState();
   // checks state and returns it. 
   // multiple calls will always be identical, 
   // except WAITING which can transition to READY or DONE.

   public State executeStep();
   // if READY, executes next step and returns getState().
   // otherwise, returns getState().

}

Мне нужно написать однопоточный планировщик, который вызывает executeStep() для «следующего» объекта. Моя проблема в том, что я точно не знаю, какую технику мне следует использовать, чтобы определить, что такое «следующий» объект. Я хочу, чтобы это было справедливо (первым пришел, первым обслужен для объектов, не находящихся в состоянии ОЖИДАНИЕ).

Моя интуиция состоит в том, чтобы иметь 3 FIFO: ГОТОВО, ЖДЕТ и ГОТОВО. Вначале все объекты помещаются в очередь READY, и планировщик повторяет цикл, в котором он берет первый объект из очереди READY, вызывает executeStep() и помещает его в очередь, соответствующую результату executeStep(). За исключением того, что элементы в очереди ОЖИДАНИЯ должны быть помещены в очередь ГОТОВО или ГОТОВО при изменении их состояния.... ааа!

Любой совет?


person Jason S    schedule 30.06.2009    source источник
comment
Вопросы: (1) может ли объект когда-либо перейти из состояния готовности на шаг назад к ожиданию? Или только от ожидания до готовности? (2) ваше описание проблемы звучит так, как будто каждый шаг ограничен по ресурсам, отсюда и необходимость в очередях и планировщике. Это правда? Доступны ли ограниченные ресурсы для Шага 2 или чего-то еще?   -  person    schedule 30.06.2009
comment
Или, когда вы говорите однопоточный планировщик, вы имеете в виду один планировщик для каждого ресурса?   -  person    schedule 30.06.2009
comment
однопоточный планировщик = я не хочу вдаваться в проблемы многопоточности (+ в моем конкретном случае, я не думаю, что смогу; я использую PHP). Сами объекты никогда не выполняют какой-либо код в фоновом режиме; единственная фоновая операция выполняется операционной системой. Объекты никогда не могут перейти из состояния готовности в ожидание.   -  person Jason S    schedule 30.06.2009
comment
Джону: да, объект может перейти из состояния готовности в ожидание, если он ожидает, пока какой-либо другой объект завершит предварительную обработку.   -  person florin    schedule 30.06.2009
comment
@флорин: ?? может в вашей ситуации, а не в моей.   -  person Jason S    schedule 30.06.2009


Ответы (6)


Если это должно быть однопоточным, вы можете использовать одну очередь FIFO для готовых и ожидающих объектов и использовать свой поток для обработки каждого объекта по мере его поступления. Если его состояние изменится на WAITING, просто поместите его обратно в очередь, и оно будет обработано повторно.

Что-то вроде (псевдокод):

var item = queue.getNextItem();
var state = item.executeStep ();
if (state == WAITING)
    queue.AddItem (item);
else if (state == DONE)
    // add to collection of done objects

В зависимости от времени, которое требуется для выполнения executeStep, вам может потребоваться ввести задержку (Sleep not for), чтобы предотвратить плотный цикл опроса. В идеале объекты должны публиковать события изменения состояния и полностью отказаться от опроса.

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

person Alex    schedule 30.06.2009
comment
Это работало очень легко, у меня было довольно простое PHP-приложение, которое мне нужно было для управления некоторыми параллельными задачами без многопоточности. Спасибо! - person Jason S; 01.07.2009

У вас нет никакого способа, чтобы объект задачи уведомил вас, когда он изменится с WAITING на READY, кроме как опросить его, поэтому очереди WAITING и READY действительно могут быть просто одним. Вы можете просто обойти его, вызывая executeStep() для каждого по очереди. Если в качестве возвращаемого значения от executeStep() вы получаете DONE, вы удаляете его из этой очереди, вставляете в очередь DONE и забываете об этом.

Если вы хотите дать «более высокий приоритет» ГОТОВЫМ объектам и попытаться пройти через все возможные ГОТОВЫЕ объекты, прежде чем тратить какие-либо ресурсы на опрос ОЖИДАНИЯ, вы можете поддерживать 3 очереди, как вы сказали, и обрабатывать очередь ОЖИДАНИЯ только тогда, когда у вас ничего нет в ГОТОВОЙ очереди.

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

person Greg Rogers    schedule 30.06.2009

Возможно, вы захотите изучить дизайн планировщика операционной системы. Посмотрите, например, на Linux и *BSD.

Несколько советов по планировщику Linux: внутри планировщика Linux и Понимание ядра Linux

person florin    schedule 30.06.2009

ПРИМЕЧАНИЕ. Это не относится к вашему вопросу о том, как планировать, но я бы использовал отдельный класс состояний, который определяет состояния и переходы. Объекты не должны знать, через какие состояния они должны пройти. Их можно проинформировать о том, на каком «Шаге» они находятся и т. д.

для этого тоже есть шаблоны.

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

Затем вы можете добавить приоритет и т. д.

person Tim    schedule 30.06.2009
comment
@xandy: Это работает. Я видел другие подобные реализации, которые подпадают под описание шаблона состояния. - person Tim; 30.06.2009

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

Для этого требуется только одна конструкция для хранения TaskObjects, и это может быть любая итерируемая структура, например. массив.

Поскольку TaskObject может асинхронно переходить из состояния WAITING в READY, вам необходимо опрашивать каждый TaskObject, о котором вы не знаете, что он выполнен.

Производительность, полученная от отсутствия опроса объектов DONE TaskObject, может быть незначительной. Это зависит от нагрузки на обработку вызова executeStep() для DONE TaskObject, которая должна быть небольшой.

Простой циклический опрос гарантирует, что после того, как READY TaskObject выполнит шаг, он не выполнит следующий шаг, пока все остальные TaskObject не получат возможность выполниться.

Одним очевидным дополнительным требованием является обнаружение, когда все TaskObjects находятся в состоянии DONE, чтобы вы могли остановить обработку.

Чтобы избежать опроса объектов задач DONE, вам необходимо либо поддерживать флаг для каждого из них, либо связывать объекты задач в две очереди: READY/WAITING и DONE.

Если вы храните TaskObjects в массиве, сделайте его массивом записей с членами DoneFlag и TaskObject.

Если по какой-то причине вы храните TaskObjects в очереди с доступными методами enqueue() и dequeue(), то накладные расходы на две очереди вместо одной могут быть небольшими.

-Al.

person A. I. Breveleri    schedule 30.06.2009

Взгляните на эту ссылку.

Ускорение конечных автоматов по сравнению с uml

Boost имеет конечные автоматы. Зачем заново изобретать?

person MadH    schedule 30.06.2009
comment
о, посты с тегом php у меня обычно игнорируются - person MadH; 01.07.2009