Вверх ↑
Ответов: 4621
Рейтинг: 746
#1: 2019-01-09 11:46:04 ЛС | профиль | цитата
nesco писал(а):
а что, если интерфейсный элемент задержится с обработкой сообщений, а лимит времени остановки параллельного потока закончится?
Конкретно в данном случае - вопрос поведения функции SendMessage(). В ней таймаутов никаких не предусмотрено. То-есть, функция возвращает управление либо после обработки сообщения главным потоком, либо в случае ошибки. Как я предполагаю, ошибка может быть сразу (например, окна не существует). А вот что будет когда сообщение попадает в очередь и ждёт обработки, но в это время конечное окно уничтожается или перед этим в очереди уже было сообщение WM_QUIT (после получения которого приложение должно завершить работу, то-есть, цикл выборки сообщений из очереди прекращается) - я не знаю. Возможно, SendMessage() тоже возвращает ошибку, но уже после некоторой задержки.
Для посылки сообщения с таймаутом ожидания есть функция SendMessageTimeout().

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

По поводу последней схемы от Neo: всё же доступ к Edit, LED и прочим визуальным компонентам из параллельных потоков, как и говорил nesco, плохая идея.
В целом ты вроде правильно используешь SafeMode, но, во-первых, "общими данными" для 2-х параллельных потоков является точка For.Position - первый SafeMode можно поставить только на запись этой точки, а второй - на чтение: чем меньше времени один поток занимает ресурс, тем меньше времени остальные потоки его ждут, тем больше полезных действий они выполнят параллельно.

Во-вторых, работая с визуальными компонентами из параллельных потоков через SafeMode ты забываешь, что кроме твоих 2 явных потоков есть ещё и третий конкурирующий поток, обращающийся к тем же визуальным компонентам - главный поток приложения, обрабатывающий сообщения компонентам от системы.
Это и является упомянутой выше проблемой доступа к визуальным компонентам из параллельных потоков. В некоторых случаях будет работать, а в некоторых будет давать ошибку. И SafeMode здесь не очень поможет - нужно использовать Synchronize, чтобы заставить ждать главный поток, пока параллельный поток работает с визуальным компонентом (на самом деле, "ждёт" параллельный поток, пока главный занимается исключительно его задачей, не отвлекаясь на другие сообщения).

Небольшое рассуждение по поводу Application.doProcessMessages из параллельного потока.
Механизм оконных сообщений Windows реализован таким образом.
Оконные сообщения обрабатываются некоторым потоком некоторого процесса. Следовательно, у этого потока есть некоторая очередь сообщений, куда система их складывает в порядке поступления. А как система знает какой поток какого процесса должен обработать сообщение данного окна? Ответ: тот поток, который и создал данное окно.
Когда приложение однопоточное, при старте система создаёт один поток ("главный поток"). Этот поток создаёт все необходимые окна (например, главную форму приложения и тулбар с кнопками в ней). Затем входит в цикл выборки сообщений из своей очереди, а следовательно, только для созданных им самим окон. Например, от тулбара приходит сообщение клика на кнопку с индексом кнопки - главный поток ищет обработчик onClick данной кнопки, по которому, например, можно открыть другое окно. И хоть это другое окно "независимо" от главного (между ними можно переключаться, перемещать и т.д.), но тем не менее оно всё равно было создано главным потоком и его сообщения приходят и обрабатываются главным потоком.

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

В связи с выше изложенным можно провести любопытный эксперимент. Может кто замечал: если обычный Timer включить из параллельного потока, то события onTimer не будет - таймер реализован через сообщения, сообщения приходят создавшему ("включившему") его потоку, но поток не обрабатывает этих сообщений (или вообще сразу завершается). Но если после включения таймера в этом же потоке начать в цикле вызывать Application.doProcessMessages, то события у таймера начнут происходить (в этом же параллельном потоке).
Наводящий вопрос: а можно ли "нормально" включить таймер из параллельного потока? Ответ - можно, "передав задачу" его включения главному потоку с помощью компонента Synchronize (или по Thread.onSyncExec).
карма: 26

1
Голосовали:nesco
Редактировалось 18 раз(а), последний 2019-01-09 13:29:00