Вверх ↑
Ответов: 4621
Рейтинг: 746
#1: 2019-01-11 12:43:47 ЛС | профиль | цитата
nesco писал(а):
А как все это безобразие приобщить к динамике, те если мне неизвестно изначальное количество потоков
Предполагаю: получить произвольное количество потоков можно только когда у нас есть динамический контейнер, в котором создаются и уничтожаются потоки? А ожидает завершения этих потоков кто-то извне, кто добавляет экземпляры контейнера, стартует их и, вероятно, отвечает за уничтожение.

Как я это понимаю.
Первое. У нас в каждый момент времени "активной" может быть только одна копия схемы - именно с ней мы работаем, когда используем внешние точки контейнера. Значит для того, чтобы "проверить состояние" каждой из схем (завершила ли она свою работу или нет), нам нужно перебрать их все извне. Сам по себе этот процесс геморройный, а результатом его работы может быть, скажем, массив хендлов компонентов Events в каждой из схем (а если у компонента Thread вывести точку Handle, то можно обойтись без Events - на ней тоже можно делать ожидание). Как ты и сказал, компонент WaitMultiple в этом случае не поможет (можно, конечно, в WaitMultiple задать заведомо максимальное количество верхних точек, к ним сверху навесить вложенную схему, которая полученный ранее "вертикальный" массив хендлов будет "раскладывать горизонтально" для подачи на точки WaitMultiple).

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

Третье. Самое актуальное и в данный момент не решенное - безопасное удаление копии схемы в контейнере, когда внутри работает параллельный поток. Очевидно, уничтожение во время работы потока неминуемо приведет к ошибке. Значит поток нужно остановить и дождаться его завершения. Как? Terminate потока в принципе недопустимо (никогда). Значит, решение - выставить каждой копии в схеме извне флаг "прекратить работу" и дождаться завершения всех потоков. Возвращемся к твоему вопросу: "как узнать, что все потоки в контейнере завершили работу?".

Вероятно, решение - тут требуется ещё один компонент по типу Events - семафор.
Принцип работы такой. Хендл семафора изначально находится в состоянии "non-signalled". Каждый поток при старте вызывает метод компонента, скажем, doAquire. Когда поток завершает свою работу, он вызывает его же метод doRelease. А внутри компонента есть объект Event и потокобезопасный счетчик. doAquire увеличивает счетчик, doRelease - уменьшает. Как только счетчик уменьшается до 0 - значит все потоки завершили работу, внутренний Event переводится в состояние "signalled". Теперь внешний по отношению к контейнеру поток может делать WaitObject на хендле данного семафора и возобновить работу, когда все потоки завершились.

Как понятно из описания, этот принцип вполне реализуется существующими компонентами: один CounterEx + Events, одноименный SafeMode у всех потоков на увеличение и уменьшение счетчика, и внешний поток с помощью WaitObject ожидает сигнала от Events. CounterEx сигналит Events, когда дошел до 0.

--- Добавлено в 2019-01-11 12:59:34

Neo писал(а):
Во втором примере не лучше поставить таймеру в сейфмод NoWait?
Если устраивает пропуск событий таймера - можно поставить. А если события параллельного потока достаточно частые, то ты рискуешь вообще непонятно когда успешно попасть таймером в критическую секцию. И чтобы попадать "чаще" тебе придется таймер значительно ускорить. Что само по себе приводит к более частой обработке главным потоком сообщений таймера.
Но дело в том что раз я обращаюсь к данным - значит они мне нужны, тогда я жду их доступности.
Neo писал(а):
Ведь Wait подвесит главный поток до освобождения ресурса.
Поэтому я в предыдущих постах упоминал частоту обращения к главному потоку и тяжесть задач выполняемых при таких обращениях. Скажем, в моем примере для информирования пользователя я поставил таймер на 500 мс. Мог бы и на 1000 - для меня это нисколько не хуже. При этом: сообщения в главный поток приходят 2 раза в секунду (или 1 раз) - это абсолютно не накладно: ты когда мышкой проводишь по окну, то у тебя в главный поток приходит сообщение чуть ли не на каждый пиксель перемещения, но это незаметно - ибо на событие мышки у тебя нет громоздкой обработки, то-есть, то что я называл "тяжесть обработки".
Так вот, заход в SafeMode 2 раза в секунду, чтение переменной и вывод её в Label - это вообще не нагрузка, даже не смотря на ожидание главного потока на SafeMode:
- параллельный поток занимает SafeMode
- главный поток стаёт на ожидание
- параллельный поток устанавливает значение Memory и покидает SafeMode (а тут уже играет роль упомянутая мной "минимизация работы, выполняемой потоками внутри SafeMode" - и это понятно: другие же потоки в это время ждут).
- после чего в SafeMode пропускается главный поток (а параллельный поток на следущем входе в SafeMode стаёт в ожидание, чтобы не нарушить данные, пока их читает другой поток).
карма: 26

0
Редактировалось 7 раз(а), последний 2019-02-04 13:27:41