
Лаб. 4 ФЛП
.docxЛабораторная работа 4
:- use_module(library(process)).
%ring(3,3,0).
%ring(5,4,1).
ring(N, M, Count) :-
process_id(MainPID),
format("Текущий процесс <~w>~n", [MainPID]),
format("=== Круг 1 ===~n"),
create_processes(N, N, Count, MainPID, ProcessList),
start_rounds(2, M, Count, ProcessList).
% Создание процессов и сохранение их PID в список
create_processes(_, 0, _, _, []) :- !.
create_processes(N, NN, Count, PrevPID, [PID|Rest]) :-
process_create(path(cmd), ['/c', 'exit'], [process(PID)]),
format("Создан процесс <~w>~n", [PID]),
(N =:= NN -> NewCount is Count ; ((N - 1) > NN -> NewCount is Count ; NewCount is Count + 1)),
%(N > NN -> NewCount is Count ; NewCount is Count),
format("<~w> получил Count=~w от <~w>~n", [PID, NewCount, PrevPID]),
process_wait(PID, _),
N1 is NN - 1,
create_processes(N, N1, NewCount, PID, Rest).
% Круги передачи (со 2-го по M-й)
start_rounds(CurrentRound, M, _, _) :-
CurrentRound > M,
%format("Все ~w кругов передачи завершены.~n", [M]),
!.
start_rounds(CurrentRound, M, Count, ProcessList) :-
format("=== Круг ~w ===~n", [CurrentRound]),
last_process(ProcessList, LastPID),
(CurrentRound =:= 2 -> Count1 is Count + 2; Count1 is Count + 1),
emulate_processes(ProcessList, Count1, LastPID, CurrentRound, M),
NextRound is CurrentRound + 1,
start_rounds(NextRound, M, Count1, ProcessList).
% Эмуляция передачи с увеличением Count
emulate_processes([], _, _, _, _) :- !.
emulate_processes([PID|Rest], Count, PrevPID, CurrentRound, M) :-
%NewCount is Count + 1,
format("<~w> получил Count=~w от <~w>~n", [PID, Count, PrevPID]),
(CurrentRound =:= M ->
format("Процесс <~w> завершился~n", [PID])
; true
),
emulate_processes(Rest, Count, PID, CurrentRound, M).
% Нахождение последнего элемента списка
last_process([X], X) :- !.
last_process([_|T], Last) :- last_process(T, Last).
:- module(parent_children, [start/1, send_to_child/2, stop/0]).
:- use_module(library(thread)).
:- dynamic child_pid/1.
% Запуск родительского процесса с N дочерними процессами
start(N) :-
retractall(child_pid(_)), % Удаляем старые идентификаторы дочерних процессов
spawn_children(N), % Создаем дочерние процессы
format('Parent started with ~d children.~n', [N]).
% Создание дочерних процессов
spawn_children(0) :-
!.
spawn_children(N) :-
N > 0,
thread_create(child_process(N), ChildID, [detached(true)]), % Создаем новый процесс
assertz(child_pid(ChildID)), % Сохраняем идентификатор дочернего процесса
N1 is N - 1,
spawn_children(N1). % Рекурсивно создаем оставшиеся процессы
% Отправка сообщения дочернему процессу
send_to_child(I, Msg) :-
findall(ChildID, child_pid(ChildID), ChildIDs),
length(ChildIDs, Length),
( I =< Length
-> nth1(I, ChildIDs, ChildID),
( thread_property(ChildID, status(running))
-> thread_send_message(ChildID, Msg),
format('Sent message to child ~d: ~w~n', [I, Msg])
; format('Child ~d is not running, removing from list.~n', [I]),
retract(child_pid(ChildID)) % Удаляем идентификатор, если поток не запущен
)
; format('Child ~d does not exist.~n', [I])
).
% Остановка всех дочерних процессов
stop_children :-
findall(ChildID, child_pid(ChildID), ChildIDs),
forall(member(ChildID, ChildIDs),
( thread_property(ChildID, status(running))
-> thread_send_message(ChildID, stop) % Отправка сообщения остановки
; format('Child thread ~w does not exist or is not running, removing from list.~n', [ChildID]),
retract(child_pid(ChildID)) % Удаляем идентификатор, если поток не запущен
)),
retractall(child_pid(_)), % Обнуляем массив child_pid
format('All children stopped and child_pid cleared.~n').
% Процесс дочернего потока
child_process(I) :-
format('Child ~d started.~n', [I]),
child_loop(I). % Запуск цикла дочернего процесса
% Цикл дочернего процесса для получения сообщений
child_loop(I) :-
findall(ChildID, child_pid(ChildID), ChildIDs),
length(ChildIDs, N),
repeat,
thread_get_message(Msg),
( Msg = stop
-> format('Child ~d stopping.~n', [N+1-I]),
retract(child_pid(ThreadID)), % Удаляем идентификатор процесса
!
; Msg = die
-> format('Child ~d dying due to die message.~n', [N+1-I]),
throw(error(child_died, child(I))) % Вызываем ошибку для перезапуска
; format('Child ~d received message: ~w~n', [N+1-I, Msg]),
fail
).
% Остановка родительского процесса
stop :-
stop_children,
format('Parent process stopped.~n').
% Перезапуск дочернего процесса в случае ошибки
:- thread_signal(all, stop). % Завершение всех потоков при завершении родителя
:- use_module(library(thread)).
:- use_module(library(lists)).
par_filter(F, List, Options, Result) :-
option(sublist_size(N), Options, 1),
option(threads(Threads), Options, unlimited),
option(timeout(T), Options, 10000000),
split_list(List, N, Sublists),
length(Sublists, TotalTasks),
effective_threads(Threads, TotalTasks, UsedThreads),
run_parallel(F, Sublists, UsedThreads, T, FilteredParts),
flatten(FilteredParts, Result).
% Разбивает список на подсписки указанного размера
split_list([], _, []).
split_list(List, N, [Sublist | Rest]) :-
length(Sublist, N),
append(Sublist, Remaining, List),
split_list(Remaining, N, Rest).
split_list(List, _, [List]) :- List \= [].
% Определяем, сколько потоков использовать
effective_threads(unlimited, Tasks, Tasks) :- !.
effective_threads(Threads, Tasks, UsedThreads) :- UsedThreads is min(Threads, Tasks).
% Запускаем параллельную обработку с использованием очереди сообщений
run_parallel(F, Sublists, Threads, Timeout, FilteredParts) :-
message_queue_create(Queue),
length(Sublists, NumSublists),
effective_threads(Threads, NumSublists, UsedThreads),
launch_threads(F, Queue, Sublists, UsedThreads, ThreadIDs),
collect_results(ThreadIDs, Queue, Timeout, FilteredParts),
message_queue_destroy(Queue).
% Запускаем потоки, но не добавляем thread_self
launch_threads(F, Queue, Sublists, Threads, ThreadIDs) :-
length(Sublists, NumSublists),
effective_threads(Threads, NumSublists, UsedThreads),
launch_threads_helper(F, Queue, Sublists, UsedThreads, ThreadIDs).
launch_threads_helper(_, _, [], _, []).
launch_threads_helper(F, Queue, [Sublist | Rest], Threads, [ThreadID | RestIDs]) :-
thread_create(process_sublist(F, Queue, Sublist), ThreadID, []),
NewThreads is Threads - 1,
launch_threads_helper(F, Queue, Rest, NewThreads, RestIDs).
% Обрабатывает подсписок и отправляет результат в очередь
process_sublist(F, Queue, Sublist) :-
include(F, Sublist, Filtered),
thread_send_message(Queue, result(Filtered)).
% Собирает результаты из потоков
collect_results([], _, _, []).
collect_results([ThreadID | Rest], Queue, Timeout, [Result | RestResults]) :-
( thread_get_message(Queue, result(Result), [timeout(Timeout)]) -> true ; Result = [] ),
thread_join(ThreadID, _),
collect_results(Rest, Queue, Timeout, RestResults).
% Пример предиката для фильтрации
filter(X) :- X > 5.
% par_filter(filter, [1,2,3,4,5,6,7,8], [sublist_size(2), threads(3), timeout(100)], Result).
% par_filter(filter, [1,2,3,4,5,6,7,8], [], Result).