- Регистрация
- 15.09.2022
- Сообщения
- 3
- Благодарностей
- 0
- Баллы
- 1
Здравствуйте! У меня банальный вопрос, но ни в ZennoPoster, ни в в ProjectMaker, ни в справке/документации — нигде не нашёл соответствующего функционала, из-за чего даже чувствую себя глупо: как выполнить какой-либо код до или после выполнения проекта?
Например, проект запускается раз в час, и перед выполнением нужно зайти на фтп, найти в папке самый свежий json файл с ссылками и распарсить его. Потом проекту программно устанавливается N количество попыток, равное количеству найденных ссылок и потоки парсят себе ссылки. А по завершению выполняется код, который собирает из этих данных json и заливает обратно на фтп.
С фтп это просто один из примеров. В начальном коде можно создавать/читать кэш, подготавливать временные папки, а в конечном — убирать временные файлы, запаковывать спарсенные данные в один архив, отправлять отчёты на почту, да и много чего ещё. Словом, функционал нужный, а где его найти — непонятно
---
Как вариант, чтобы реализовать этот функционал, можно сделать три отдельных проекта (условно: Start.zp, Thread.zp, Finish.zp): Start запускается в однопотоке, заходит на фтп, а по завершению создаёт «файл-сигнал», который запускает Thread. Либо Start находит id проекта Thread и задаёт ему количество попыток через
Но вот как понять, когда кончаются все Thread и когда пора запускать Finish? Я пробовал следующее:
Некоторые методы работают отлично в однопоточном режиме, но как только в зеннопостере в поле «Максимум потоков» попадает число больше единички — начинаются проблемы (чаще всего в виде многократного вызова финального кода).
Есть ещё вариант создать отдельный, четвёртый проект чисто для мониторинга других, который будет в цикле проверять
Например, проект запускается раз в час, и перед выполнением нужно зайти на фтп, найти в папке самый свежий json файл с ссылками и распарсить его. Потом проекту программно устанавливается N количество попыток, равное количеству найденных ссылок и потоки парсят себе ссылки. А по завершению выполняется код, который собирает из этих данных json и заливает обратно на фтп.
С фтп это просто один из примеров. В начальном коде можно создавать/читать кэш, подготавливать временные папки, а в конечном — убирать временные файлы, запаковывать спарсенные данные в один архив, отправлять отчёты на почту, да и много чего ещё. Словом, функционал нужный, а где его найти — непонятно

---
Как вариант, чтобы реализовать этот функционал, можно сделать три отдельных проекта (условно: Start.zp, Thread.zp, Finish.zp): Start запускается в однопотоке, заходит на фтп, а по завершению создаёт «файл-сигнал», который запускает Thread. Либо Start находит id проекта Thread и задаёт ему количество попыток через
ZennoPoster.SetTries()
. Либо можно не дробить проект и обработать всё через lock (SomeObject) {}
: кто первый в лок вошёл, тот фтп и занимается, а остальные потоки ждут, а потом просто обходят этот участок кода стороной благодаря какой-нибудь переменной. В общем, с инициирующим кодом проблем нет, хоть все способы и ощущаются несколько «костыльно».Но вот как понять, когда кончаются все Thread и когда пора запускать Finish? Я пробовал следующее:
- В GoodEnd каждого потока добавлял следующий код:
C#:
lock (Locks.Finalize) // В общем коде `public class Locks` чисто с набором объектов для лока
{
foreach (string task in ZennoPoster.TasksList)
{
project.Xml.FromString($"<Task>{task}</Task>");
bool isThisTask = project.Xml.Task.Id.Value == project.TaskId;
if (!isThisTask) continue;
int triesCount = int.Parse(project.Xml.Task.ExecutionSettings.NumberOfTries.Value);
if (triesCount == 0)
{
// У проекта осталось 0 попыток, можно выполнить финальный код...?
}
}
}
Но код опирается количество оставшихся попыток, что означает, что к моменту выполнения этого кода сразу несколько потоков могут получить значение 0, что неприемлемо. А ещё к концу выполнения может случиться какой-то рассинхрон (очистка памяти?) и
Locks.Finalize
указывает на разные переменные, из-за чего лок вообще может не сработать.- В прошлом коде менял проверку оставшихся попыток на 3 (потому что числа ниже почему-то могли проскакивать при слишком быстром завершении потока). Если
triesCount <= 3
, значит скоро должен наступить конец процесса (можно и без этой проверки, но тогда одному потоку придётся подлня висеть в ожидании): один из потоков через лок входит в режим ожидания и каждые 250мс опрашивает, сколько осталось запущенных инстансов:
C#:
...
// "Monitor.*" делает то же, что и lock, только более гибко
if (triesCount <= 3 && !Monitor.IsEntered(Locks.LastThreadWatcher)) {
Monitor.Enter(Locks.LastThreadWatcher);
while (true)
{
int instancesCount = ZennoPoster.AllInstances.Count;
if (instancesCount == 1)
{
Monitor.Leave(Locks.LastThreadWatcher);
// Остался один инстанс, который принадлежит текущему потоку, можно выполнить финальный код...?
}
else
{
Thread.Sleep(250);
}
}
}
...
Но и тут что-то пошло не по плану:
ZennoPoster.AllInstances
, как неожиданно, возвращает список всех инстансов, даже тех, которые текущему проекту не принадлежат. А список инстансов текущего проекта, или какой-нибудь метод для фильтрации ZennoPoster.AllInstances
найти не удалось. К тому же этот способ если бы и работал, то всё равно он опирается на браузер и в безбраузерном режиме работал бы некорректно.- Пробовал совсем сомнительные методы по типу «если временная папка не обновлялась дольше 30 секунд, значит все закончили свою работу и можно запускать финальный код», но это совсем уж несерьёзное решение.
Некоторые методы работают отлично в однопоточном режиме, но как только в зеннопостере в поле «Максимум потоков» попадает число больше единички — начинаются проблемы (чаще всего в виде многократного вызова финального кода).
Есть ещё вариант создать отдельный, четвёртый проект чисто для мониторинга других, который будет в цикле проверять
Task.ExecutionSettings.Status
: если у Start.zp статус Complete
, то запускать Thread.zp, а когда у Thread.zp статус Complete
, то запускать Finish.zp... но, может быть, всё-таки есть адекватный способ выполнять свой код до/после потоков?