Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Хьюз Камерон. Параллельное и распределенное программирование на С++ - royallib.ru.doc
Скачиваний:
117
Добавлен:
11.03.2016
Размер:
1.97 Mб
Скачать

Порождение источников знаний в конструкторе «классной доски»

«Классная доска» реализуется как распределенный объект, использующий CORBA-протокол. В данном случае одной из основных целей «классной доски» является порождение источников знаний. Это важный момент, поскольку «классная доска» должна иметь доступ к идентификационным номерам задач. Начальное состояние «классной доски» (оно устанавливается в конструкторе) включает информацию о студенте, его академической характеристике, текущем семестре, требованиях для получения диплома и т.д. С помощью «классной доски», исходя из начального состояния, определяется, какие источники знаний следует запустить в работу. Иначе говоря, оценив начальную задачу и исходное состояние системы, «классная доска» составляет список запускаемых на выполнение источников знаний. Каждый источник знаний имеет соответствующий двоичный файл, а для хранения имен этих файлов «классная доска» использует контейнер Solvers. Позже, при функционировании конструктора, с по м ощью функционального объекта (или объекта-функции) и алгоритма for_each() порождаются источники знаний. Вспомните, что любой класс, в котором определена операторная функция operator(), м ож н о испо л ьзовать как функциональный объект. Объекты-функции, как прави л о при м еняют сов м естно со стандартны м и алгорит м а м и в м есто функций и л и в допо л нение к ни м. Обычно везде, где м ожно использовать обычную функцию, ее м ожно за м енить объекто м -функцией. Чтобы определить собственный функциональный объект, необходи м о определить операторный м етод operator (), придав е м у соответствующий с м ысл, указав список пара м етров и тип возвращае м ого и м значения. Наша CORBA-реализация «классной доски» м ожет под д ерживать источники знаний, реализованные с по м ощью PVM-задач, традиционных UNDC/Linux-задач или от д ельных потоков, использующих библиотеки POSIX thread. По типу задач, порождае м ых в конструкторе, м ожно определить, с каки м и и м енно задача м и будет работать «классная доска»: с POSIX-потока м и, традиционны м и UNIX/Linux-процесса м и или PVM-задача м и.

Порождение источников знаний с помощью pvm-задач

Конструктор «классной доски» содержит следующий вызов алгоритма, for_each(Solve.begin(),Solve.end(), Task);

Алгоритм for_each () применяет операторный метод объекта функции (созданного для класса задачи) к каждому элементу контейнера Solve. Этот метод используется для порождения источников знаний в соответствии с моделью MIMD, при реализации которой все источники знаний имеют различную специализацию и работают с различными наборами данных. Объявление этого класса задач приведено в листинге 13.4.

// Листинг 13.4. Объявление класса задачи

class task{

int Tid[4];

int N;

//. . .

public:

//. . .

task(void) { N = 0; } void operator()(string X);

};

void task::operator()(string X) {

int cc; pvm_mytid();

cc = pvm_spawn(const_cast<char *>(X.data()),NULL,0,"",l,&Tid[N]);

N++;

}

blackboard::blackboard(void) {

task Task;

vector<string> Solve;

//.. .

// Determine which KS to invoke

//. . .

Solve.push_back(KS1);

Solve.push_back(KS2);

Solve.push_back(KS3);

Solve.push_back(KS4);

for_each(Solve.begin(), Solve.end(), Task);

}

Этот класс task инкапсулирует порожденный процесс. Он содержит идентификационный но м ер задачи (поскольку у нас используется PVM-задача). В случае при м енения стандартных UNDC/Linux-процессов или Pthread-потоков, он должен содержать идентификационный но м ер процесса или потока. Этот класс действует как интерфейс между создаваемым процессом или потоком и «классной доской». «Классная доска» здесь является основным компонентом управления. Она может управлять PVM-задачами с помощью их идентификационных номеров. Кроме того, «классная доска» может использовать групповые PVM-операции для синхронизации PVM-задач с использованием барьеров, организации PVM-задач в логические группы, которые должны отрабатывать определенные аспекты решаемой задачи, и сигнализации членов группы с помощью соответствующих тегов сообщений. Групповые PVM-операции перечислены и описаны в табл. 13.2.

Особый интерес для нашей «классной доски» представляют операции pvm_barrier() и pvm_joingroup(), поскольку существуют ситуации, в которых «классная доска» не запускает новые источники знаний до тех пор, пока определенная группа источников знаний не завершит свою работу. Для блокирования вызывающего процесса до нужного момента (до окончания обработки данных соответствующими источниками знаний) можно использовать операцию pvm_barrier (). Например, «классная доска» в качестве консультанта по выбору курсов обучения не будет активизировать источник знаний, отвечающий за составление расписания, до тех пор, пока не представят свои предложения источники знаний, которые специализируются на основных, общеобразовательных, второстепенных и факультативных курсах. Поэтому «классная доска» будет использовать операцию pvm_barrier () для ожидания завершения работы этой группы PVM-задач. На рис. 13.5 представлена UML-диаграмма видов деятельности, которая позволяет понять, как синхронизируются источники знаний и «классная доска».

Барьер синхронизации здесь реализуется с помощью операций pvm_barrier () и pvm_joingroup (). Реализация операторной функции для объекта задачи приве д ена в л истин г е 13.5.

Таблица 13.2. Групповые PVM-операции

int pvm_joingroup (char *groupname); Вносит вызывающий процесс в группу groupname, а затем возвращает int-значение, которое представляет собой номер процесса в этой группе

int pvm_lvgroup (char *groupname); Удаляет вызывающий процесс из группы groupname

int pvm_gsive (char *groupname); Возвращает int-значение, которое представляет собой количество членов в группе groupname

int pvm_gettid (char *groupname, int inum); Возвращает int-значение, равное идентификационному номеру задачи, выполняемой процессом, который идентифицируется именем группы groupname и номером экземпляра inum

int pvm_getinst (char *groupname, int taskid); Возвращает int-значение, которое представляет собой номер экземпляра, связанный с именем группы groupname и процессом, выполняющим задачу с идентификационным номером taskid

int pvm_barrier (char *groupname, int count); Блокирует вызывающий процесс до тех пор, пока count членов в группе groupname не вызовут эту функцию

int pvm_bcast (char *groupname, int messageid); Передает всем членам группы groupname сообщение, хранимое в активном буфере отправки, связанном с номером messageid

int pvm_reduce (void *operation, void *buffer, int count, int datatype, int messageid, char *groupname, int root); Выполняет глобальную операцию operation во всех процессах группы groupname

// Листинг 13.5. Определение функции operator() // в классе task

void task::operator()(string X) {

int cc; pvm_mytid();

cc = pvm_spawn(const_cast<char *>(X.data()),NULL,0,"",l, &Tid[N]);

N++;

}

Функция-оператор operator () используется для порождения PVM-задач. Имя задачи содержится в элементе X. data (). При обращении к функции pvm_spawn () (см. листинг 13.5) создается одна задача, а ее идентификационный номер сохраняется в элементе Tid[N] . (Подробнее о функции pvm_spawn () и вызове PVM-задач см. гла-вуб.) Класс task используется для создания функциональных объектов (объектов-функций). При выполнении алгоритма

for_each(Solve.begin(),Solve.end(),Task);

вызывается функция operator (), которая выполняет объект Task. Эта операция заставляет активизироваться источники знаний, содержа щ иеся в контейнере Solve. Алгоритм for_each () гарантирует активизацию всех источников знаний. Если используется м одель SIMD, то в алгоритме for_each () нет никакой необходимости. Вместо него прямо в конструкторе «классной доски» мы используем вызов функции pvm_spawn(). В листинге 13.6 как раз и показано, как при использовании модели SIMD можно запустить множество PVM-задач из конструктора «классной доски».

// Листинг 13.6. Запуск PVM-задач из конструктора

// класса task

void task::operator()(string X) {

int cc; pvm_mytid();

cc = pvm_spawn(const_cast<char *>(X.data()),NULL,0,"",l, &Tid[N]);N++;

}