Выполнить некий код единожды перед всеми потоками / после всех потоков.

Nobody

Client
Регистрация
15.09.2022
Сообщения
3
Благодарностей
0
Баллы
1
Здравствуйте! У меня банальный вопрос, но ни в ZennoPoster, ни в в ProjectMaker, ни в справке/документации — нигде не нашёл соответствующего функционала, из-за чего даже чувствую себя глупо: как выполнить какой-либо код до или после выполнения проекта?

Например, проект запускается раз в час, и перед выполнением нужно зайти на фтп, найти в папке самый свежий 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... но, может быть, всё-таки есть адекватный способ выполнять свой код до/после потоков?
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 710
Баллы
113
много буковок... не осилил....
ZennoPoster.GetThreadsCount я вот этим методом проверяю количество работающих потоков и когда они будут нулем, тогда перехожу к финальным действиям.
достаточно 1 дополнительного шаблона - диспетчера, который делает предварительные действия, запускает рабочие потоки, контролит работу, поддерживает нужное количество рабочих потоков, выдает метки на завершение работы, выполняет финальные действия.
до кучи диспетчер может быть базой для отправки данных в сторонние приложения для отображения статистики или получения команд из этого приложения.
 
  • Спасибо
Реакции: djaga

Nobody

Client
Регистрация
15.09.2022
Сообщения
3
Благодарностей
0
Баллы
1
Ну так, буковки специально чтобы дать понять, что уже много чего перепробовал и на форум стучусь не от скуки :-)

ZennoPoster.GetThreadsCount я вот этим методом проверяю количество работающих потоков и когда они будут нулем, тогда перехожу к финальным действиям.
С ZennoPoster.GetThreadsCount ровно та же проблема, что и с ZennoPoster.AllInstances.Count: если параллельно работает ещё какой-то шаблон, совершенно не связанный с моим, то оно учитывает и его потоки. Один шаблон запущен в 3 потока, какой-то другой в три, и вот уже ZennoPoster.GetThreadsCount == 6. К тому же если будет работать несколько шаблонов/диспетчеров одновременно, ожидающих нуля в этой переменной, то они никогда друг друга не дождутся и зависнут в бесконечном цикле, разве нет?
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 710
Баллы
113
Ну так, буковки специально чтобы дать понять, что уже много чего перепробовал и на форум стучусь не от скуки :-)



С ZennoPoster.GetThreadsCount ровно та же проблема, что и с ZennoPoster.AllInstances.Count: если параллельно работает ещё какой-то шаблон, совершенно не связанный с моим, то оно учитывает и его потоки. Один шаблон запущен в 3 потока, какой-то другой в три, и вот уже ZennoPoster.GetThreadsCount == 6. К тому же если будет работать несколько шаблонов/диспетчеров одновременно, ожидающих нуля в этой переменной, то они никогда друг друга не дождутся и зависнут в бесконечном цикле, разве нет?
я написал, что пользуюсь этим методом, значит пользуюсь, а не теоретически выложил метод. У меня работают десятки проектов и у них нет проблем с определением количества потоков.
Почитайте внимательнее про эти методы, что принимают на вход. Погуглите эти методы на форуме что ли ....

97701
 
  • Спасибо
Реакции: Sho

Nobody

Client
Регистрация
15.09.2022
Сообщения
3
Благодарностей
0
Баллы
1
Почитайте внимательнее про эти методы, что принимают на вход. Погуглите эти методы на форуме что ли ....
А, он принимает аргументы. Тогда прошу прощения за невнимательность, просто на Assemblies Documentation про этот метод ни слова, а на всплывающую подсказку что-то не обратил внимания и принял отсутствие аргументов как данность.

С ними, похоже, всё работает как и должно, спасибо большое за ответ!
 

Кто просматривает тему: (Всего: 2, Пользователи: 0, Гости: 2)