OberonCore
https://forum.oberoncore.ru/

Как сделать аналог Wait?
https://forum.oberoncore.ru/viewtopic.php?f=23&t=1426
Страница 1 из 1

Автор:  Rifat [ Понедельник, 30 Март, 2009 12:23 ]
Заголовок сообщения:  Как сделать аналог Wait?

Есть программа, которая должна скачивать из сети несколько файлов. Сначала были использованы блокирующие функции для работы с сетью, основным недостатком данного метода было то, что интерфейс пользователя никак не реагировал, в то время, пока работали блокирующие функции. После чего плокирующие функции были заменены на неблокирующие, при этом интерфейс пользователя перестал блокироваться. Теперь требуется скачивать файлы последовательно (то есть следующий файл начинает скачиваться только после предыдущего), но в неблокирующем режиме.
Одним из вариантов является следующий псевдокод:

PROCEDURE (c: NetworkClient) Do-;
VAR Net2: NetworkClient;
BEGIN
IF файл скачался THEN
(* Скачиваем следующий файл *)
NEW(Net2);
Net2.GetFile(Какие-то параметры);
Services.DoLater(Net2, Services.Ticks() + Services.resolution);
ELSE
Services.DoLater(c, Services.Ticks() + Services.resolution);
END

END Do;

PROCEDURE Start;
VAR Net: NetworkClient;
BEGIN
NEW(Net);
Net.GetFile(какие-то параметры);
Services.DoLater(Net, Services.Ticks() + Services.resolution);
END Start;

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

PROCEDURE Start;
VAR i: INTEGER;
Net: NetworkClient;
BEGIN
New(Net);
FOR i := 0 TO N DO
Net.GetFile(Address[i], SaveAsFile[i]);
Services.DoLater(Inet, Services.Ticks() + Services.resolution);
Services.WaitUntilEnd();
END;
END Start;

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

Можно ли такое сделать? Может быть есть какие-нибудь другие похожие методы, решающие ту же проблему?

Автор:  Пётр Кушнир [ Понедельник, 30 Март, 2009 14:46 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Я бы сделал так:

Код:
DownloadAction = POINTER TO RECORD(Services.Action)
   list : (* список файлов для скачивания, и всего, что необходимо для работы *)
   currentFile : (* какой либо указатель на текущий файл*)
END;

PROCEDURE(da : DownloadAction) Do;
BEGIN
   IF ФайлЗагружен(da.currentFile) THEN
      da.currentFile:=СледующийФайл();
   ELSIF ~ФайлЗагружен(da.curentFile) THEN
      ЗагрузитьЧасть(da.currentFile)
   END;
   IF ~ВсеФайлыЗагружены(da.list) THEN Services.DoLater(da, Services.now) END
END Do;

PROCEDURE Start;
VAR da : DownloadAction;
BEGIN
   NEW(da); da.list:=... da.currsntFile:=(первый файл)
   Services.DoLater(da, Services.now) (запускаем наше действие в свободное плавание)
END Start;


Такой вопрос, а вы используете подсистему Comm для работы с сетью? Насколько я знаю, она изначально неблокирующая.

Автор:  Rifat [ Понедельник, 30 Март, 2009 15:33 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Пётр Кушнир писал(а):
Такой вопрос, а вы используете подсистему Comm для работы с сетью? Насколько я знаю, она изначально неблокирующая.


Я использовал модуль CommTCPAsync с сайта: http://www.zinnamturm.eu/downloadsDH.htm#Dbu
Хотя этот модуль решает проблему блокировки функций, при создании подключения, функция GetFile из этого модуля блокирует работу среды до полного скачивания файла. Но там же в комментариях сказано, что это легко переделать на неблокирующую работу, что я и сделал.

Насчет вашего решения, в принципе оно похоже на первое решение, которое я привёл. Я знаю, что так можно сделать. Но почему-то мне такое решение кажется некрасивым, так как вместо простого циклического подхода используется рекурсивный подход.

Я почитал форум, понял что, вопрос, который я задал, лучше было бы задать в секции: "Параллельное и многопоточное программирование". Понял что тема параллельного программирования достаточно сложна, но тем не менее прочитанное натолкнуло меня на еще одно решение. Здесь приведен простенький модуль, который должен сначала распечатать цифры от 0 до 4 с интервалом в одну секунду, после чего распечатать прочерки. Суть решения в вызове Services.actionHook.Step в цикле, что как я думаю и является аналогом функции Wait о которой я спрашивал.

MODULE MyAsyncModule;

IMPORT Log, Services;

TYPE
Async = POINTER TO RECORD (Services.Action)
counter: INTEGER;
END;

VAR
b: BOOLEAN;

PROCEDURE (a: Async) Do-;
BEGIN
IF a.counter < 5 THEN
Log.Int(a.counter); Log.Ln;
INC(a.counter);
Services.DoLater(a, Services.Ticks()+Services.resolution);
ELSE
b := TRUE;
END;
END Do;

PROCEDURE Start*;
VAR a: Async;
BEGIN
b := FALSE;
NEW(a);
a.counter := 0;
Services.DoLater(a, Services.Ticks()+Services.resolution);
WHILE ~b DO
Services.actionHook.Step;
END;
Log.String("-------------------------"); Log.Ln;
END Start;

END MyAsyncModule.

Насколько правильно/неправильно данное решение? Какие могут быть минусы?

Автор:  Пётр Кушнир [ Понедельник, 30 Март, 2009 16:12 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Rifat писал(а):
вместо простого циклического подхода используется рекурсивный подход
Нет, по моему, получается как раз цикл, нигде нет рекурсивного вызова, посмотрите внимательнее, Services.DoLater это не вызов, это добавление в очередь.

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

с CommTCPAsync я как-то работал, у него какие-то проблемы с Vista были, поэтому отказался. По-моему вам стоит поработать с CommTCP, и возможно с буферами от А. Ильина. Хотя бы в режиме демки, так станет понятнее, как работать с сетью.

Я когда осваивал, наклепал себе демку, может вам пригодится:
В модуле ObxStreamsServer надо запустить коммандер Start, после этого в модуле ObxStreamsClient надо запустить коммандер Send -- получится пересылка файла. Поможет понять принцип.

Вложения:
Mod.zip [4.34 КБ]
Скачиваний: 409

Автор:  Rifat [ Понедельник, 30 Март, 2009 16:23 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Пётр Кушнир писал(а):
Rifat писал(а):
вместо простого циклического подхода используется рекурсивный подход


с CommTCPAsync я как-то работал, у него какие-то проблемы с Vista были, поэтому отказался. По-моему вам стоит поработать с CommTCP, и возможно с буферами от А. Ильина. Хотя бы в режиме демки, так станет понятнее, как работать с сетью.

Я когда осваивал, наклепал себе демку, может вам пригодится:

На Vista я пока не тестировал, может когда буду тестировать на Vista ваша демка пригодиться.

Насчет последовательности скачиваний, думаю, все-таки буду делать как в вашем примере.

То решение, которое я нашел:
WHILE ~b DO
Services.actionHook.Step;
END;
работает, конечно, лучше, чем WHILE ~b DO END;
но интерфейс пользователя все равно блокируется, думаю что надо бы добавить еще одну команду в цикл
WHILE ~b DO
Services.actionHook.Step;
Обработать оконные события;
END;
но не знаю какую функцию вызвать.

Автор:  Пётр Кушнир [ Понедельник, 30 Март, 2009 16:28 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Порекомендую вам изучить демку прямо сейчас, она к висте не имеет отношения.
Не поверите, но мне вообще не надо в своём приложении обрабатывать оконные события, обращаться к Services.actionHook. В демке как раз использование общих, доступных средств. вместо вывода в лог вы можете вызывать Dialog.Update для интерактора прогресс-бара.

Автор:  Rifat [ Понедельник, 30 Март, 2009 16:59 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Пётр Кушнир писал(а):
Порекомендую вам изучить демку прямо сейчас, она к висте не имеет отношения.
Не поверите, но мне вообще не надо в своём приложении обрабатывать оконные события, обращаться к Services.actionHook. В демке как раз использование общих, доступных средств. вместо вывода в лог вы можете вызывать Dialog.Update для интерактора прогресс-бара.


Посмотрел вашу реализацию, в общем понятно что и как работает. Единственное, что у Вас модуль ...Client занимается отправкой файла, а мне же требуется скачивать файл.

Я понимаю, что чтобы сделать скачивание нескольких файлов в общем случае не нужны ни Services.actionHook, ни обработка оконных сообщений. Просто мне кажется, что с перенесением работы в функцию Do и использованием очереди получается немного запутанно, если новичек будет читать такой код, то он не сразу разберется. Согласен что с очередью очень хорошее решение, а главное правильное, при текущих ограничениях BlackBox-а.

Но у меня возникла мысль, что, возможно, как-то можно обойтись без очереди.
Цель была такой, чтобы код выглядел примерно так:

Установить адрес скачиваемого файла;
Начать скачивание файла в фоновом режиме;
Дождаться завершения скачивания файла;
Установить адрес другого скачиваемого файла;
Начать скачивание файла в фоновом режиме;
...

Но оказалось, что сделать функцию "Дождаться завершения скачивания файла", которая не будет блокировать работу всей среды, не так-то просто, а без нее получается, что 2 файла будут скачиваться параллельно, что иногда не очень желательно, особенно, если файлов не 2, а 50 или даже больше.

Автор:  Пётр Кушнир [ Понедельник, 30 Март, 2009 17:13 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Rifat писал(а):
Client занимается отправкой файла, а мне же требуется скачивать файл
Вопрос реализации.
Rifat писал(а):
Просто мне кажется, что с перенесением работы в функцию Do и использованием очереди получается немного запутанно, если новичек будет читать такой код, то он не сразу разберется.
По-моему Отложенное Действие(Action) + переназначение как раз подходят для асинхронной работы, когда наперёд не знаешь, когда прилетит пакет из сети. Это довольно просто понять даже новичку. Ну и, необязательно ведь переносить всё в Do.
Rifat писал(а):
при текущих ограничениях BlackBox-а.
При каких?

Автор:  Rifat [ Понедельник, 30 Март, 2009 17:24 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Пётр Кушнир писал(а):
Rifat писал(а):
при текущих ограничениях BlackBox-а.
При каких?

Отсутствие функции Wait, о которой я спрашивал :)

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

Автор:  Пётр Кушнир [ Понедельник, 30 Март, 2009 17:34 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

:D Тут возникает другой вопрос: А верно ли предположение о том, что такая функция должна присутствовать? Ведь фоновых задач(в том смысле, в котором их предполагает многозадачность) в ББ нет.

Но, как вы видите, и вашу задачу можно решить без многопоточности и без wait.

Автор:  Info21 [ Понедельник, 30 Март, 2009 18:32 ]
Заголовок сообщения:  Re: Как сделать аналог Wait?

Пётр Кушнир писал(а):
Я когда осваивал, наклепал себе демку, может вам пригодится:
Спасибо, пригодится.

Страница 1 из 1 Часовой пояс: UTC + 3 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/