Дано число X. Создать две программы. Запущенные в произвольном кол-ве экземпляры первой программы циклически ожидают ввода строки, в которой подсчитывают кол-во единиц. Экземпляр второй программы ожидает, пока суммарное количество введенных единиц во всех экземплярах первой программы не достигнет или не превысит значения X. После чего сигнализирует об этом факте. Коммуникация программ друг с другом - только с помощью системных семафоров или событий. Во время работы пользовательский ввод программ не блокируется. Порядок запуска программ - произвольный.
Язык и ОС разрешил использовать любой. Студент выбрал C# и Windows. Отвел ему на решение одну пару. Не решил. Дал еще две пары. Не решил. Разрешил решать до конца дня. Не решил. Ну, думаю, ладно - детский сад штаны на лямках, отпущу до понедельника (дело было в пятницу), может ему кто поможет. Сегодня пришло в голову - может сложную задачку дал? Попробовал решить сам. Оказалось не сложно, но интересно. 15 минут на решение, 30 на отладку (подзабыл о персистентости именованных posix-семафоров). Вот что вышло:
Число X содержится в текстовом файле x.txt
Первая программа:
Подсветка синтаксиса языка pascal
{$mode objfpc}
uses libc;
var t:text; x:integer;
s:string;
curr_set:integer=0;
var sem:psem_t;
procedure am(cnt:integer);
var value,res:longint;
begin
sem_getvalue(sem,@value);
writeln('Захватываем семафорчик в кол-ве ',cnt,' блокировок. На семафоре осталось ~ ',value);
while cnt<>0 do begin
res:=sem_trywait(sem);
if res<>0 then begin
sem_getvalue(sem,@value);
writeln('Не срослось захватить все, конкуренты помешали.');
writeln('Незахватилось ' ,cnt,' блокировок. На семафоре осталось ~ ',value);
break;
end;
inc(curr_set);
dec(cnt);
end;
sem_getvalue(sem,@value);
writeln('Захватили семафорчик в кол-ве ',curr_set,' блокировок. На семафоре осталось ~ ',value);
end;
procedure tfu();
var value:longint;
begin
sem_getvalue(sem,@value);
writeln('Отдаем блокировки в кол-ве ',curr_set,' блокировок. На семафоре сейчас ~ ',value);
while curr_set<>0 do begin
sem_post(sem);
dec(curr_set);
end;
sem_getvalue(sem,@value);
writeln('Отдали блокировки. На семафоре осталось ~ ',value);
end;
function cnt(s:string):integer;
var i:integer;
begin result:=0; for i:=1 to length(s) do if s[i]='1' then inc(result); end;
begin
assign(t,'x.txt');reset(t);readln(t,x);close(t);
sem:=sem_open('test',O_CREAT,511,x);
while true do begin
readln(s); if s='' then break;
tfu();
am(cnt(s));
end;
tfu();
sem_close(sem);
//sem_unlink('test');
end.
uses libc;
var t:text; x:integer;
s:string;
curr_set:integer=0;
var sem:psem_t;
procedure am(cnt:integer);
var value,res:longint;
begin
sem_getvalue(sem,@value);
writeln('Захватываем семафорчик в кол-ве ',cnt,' блокировок. На семафоре осталось ~ ',value);
while cnt<>0 do begin
res:=sem_trywait(sem);
if res<>0 then begin
sem_getvalue(sem,@value);
writeln('Не срослось захватить все, конкуренты помешали.');
writeln('Незахватилось ' ,cnt,' блокировок. На семафоре осталось ~ ',value);
break;
end;
inc(curr_set);
dec(cnt);
end;
sem_getvalue(sem,@value);
writeln('Захватили семафорчик в кол-ве ',curr_set,' блокировок. На семафоре осталось ~ ',value);
end;
procedure tfu();
var value:longint;
begin
sem_getvalue(sem,@value);
writeln('Отдаем блокировки в кол-ве ',curr_set,' блокировок. На семафоре сейчас ~ ',value);
while curr_set<>0 do begin
sem_post(sem);
dec(curr_set);
end;
sem_getvalue(sem,@value);
writeln('Отдали блокировки. На семафоре осталось ~ ',value);
end;
function cnt(s:string):integer;
var i:integer;
begin result:=0; for i:=1 to length(s) do if s[i]='1' then inc(result); end;
begin
assign(t,'x.txt');reset(t);readln(t,x);close(t);
sem:=sem_open('test',O_CREAT,511,x);
while true do begin
readln(s); if s='' then break;
tfu();
am(cnt(s));
end;
tfu();
sem_close(sem);
//sem_unlink('test');
end.
Вторая программа:
Подсветка синтаксиса языка pascal
{$mode objfpc}
uses cthreads,sysutils,libc,classes;
type
tsemthread=class(tthread)
procedure execute; override;
end;
procedure tsemthread.execute;
var sem:psem_t;
value:longint;
begin
repeat
if terminated then exit;
sem:=sem_open('test',0,511,1);
sleep(1);
until sem<>nil;
while true do begin
if terminated then break;
sem_getvalue(sem,@value);
if value=0 then write('YES!!!!');
sleep(1);
end;
sem_close(sem);
end;
var
t:tsemthread;
s:string;
begin
t:=tsemthread.create(false);
while true do begin
readln(s); if s='' then break;
writeln(s);
end;
t.terminate;
t.waitfor;
t.destroy;
end.
uses cthreads,sysutils,libc,classes;
type
tsemthread=class(tthread)
procedure execute; override;
end;
procedure tsemthread.execute;
var sem:psem_t;
value:longint;
begin
repeat
if terminated then exit;
sem:=sem_open('test',0,511,1);
sleep(1);
until sem<>nil;
while true do begin
if terminated then break;
sem_getvalue(sem,@value);
if value=0 then write('YES!!!!');
sleep(1);
end;
sem_close(sem);
end;
var
t:tsemthread;
s:string;
begin
t:=tsemthread.create(false);
while true do begin
readln(s); if s='' then break;
writeln(s);
end;
t.terminate;
t.waitfor;
t.destroy;
end.
В случае аварийного останова первой программы захваченный семафор не освобождается, что, естественно, скажется на корректности работы. В принципе, устойчивости к аварийным ситуациям от программы не требовалось, однако для подчистки "хвостов" можно использовать, например, следующую программу:
Подсветка синтаксиса языка pascal
{$mode objfpc}
uses libc;
begin
sem_unlink('test');
end.
uses libc;
begin
sem_unlink('test');
end.
Другой вариант заключается в организации реакции на сигналы завершения, но было уже лень это делать, ибо сути программы обработка сигналов не изменит, да и спать уже хочется . Посмотрим, что мне завтра (т.е. уже сегодня) принесет студент. Подозреваю, что отговорки, но как знать...