Вверх ↑
Этот топик читают: Гость
Ответов: 704
Рейтинг: 7
#16: 2019-01-07 12:11:08 ЛС | профиль | цитата
nesco, благодарю за подробные разъяснения и примеры! Это настоящий рождественский подарок для hiasm-щика. На мультипоточную схему, пусть и непроверенную, с удовольствием посмотрю.

Редактировалось 1 раз(а), последний 2019-01-07 12:11:51
карма: 0

0
Разработчик
Ответов: 26061
Рейтинг: 2120
#17: 2019-01-07 13:34:32 ЛС | профиль | цитата
Neo писал(а):
На мультипоточную схему, пусть и непроверенную, с удовольствием посмотрю.

MultiDownLoadURL_006
Но нужно обновить HTTP_Get, я там ввел возможность менять UserAgent-a в RealTime-e.

Редактировалось 1 раз(а), последний 2019-01-07 13:37:34
карма: 22

0
файлы: 1MultiDownLoadURL_006.zip [7.9KB] [474]
Ответов: 704
Рейтинг: 7
#18: 2019-01-08 00:00:04 ЛС | профиль | цитата
Оптимизирую "джунгли" по новым "понятиям" о потоках. Стало уже стабильнее. Но разбираясь с примером выше, обнаружил что идет работа вообще без мьютексов. И обратился к штатному примеру, вспомнить как его вообще применять. Так получается что выводить в оконные элементы можно из параллельного потока прямо по onExec? Да и читать данные из того же Text тоже можно в параллельном потоке? Считал что обращаться к оконным элементам из параллельного вообще нельзя, только через синхронизированные с главным потоком события. Может есть что почитать на эту тему применимо к HiAsm?

Штатную схему прикрепляю.

Add(MainForm,14017285,70,133)
{
Left=20
Top=105
Width=424
Height=79
Caption=""
link(onCreate,7461548:doEvent1,[])
}
Add(InfoTip,16646216,329,91)
{
Info=#15:Защищаем ресурс|0:|0:|0:|0:|0:|0:|0:|42:По окончанию работы освобождаем для других|
Width=141
Height=137
}
Add(For,9010111,532,168)
{
End=100000
IncludeEnd=1
link(onEvent,3731085:doEvent1,[])
link(onStop,6614406:doOff,[(578,181)(578,114)(520,114)(520,132)])
}
Add(Edit,13816107,679,168)
{
Left=10
Top=13
Width=95
Text=""
}
Add(Hub,3731085,588,168)
{
link(onEvent1,13816107:doText,[])
link(onEvent2,15914761:doWork2,[])
}
Add(Application,1559756,637,175)
{
Wait=1
}
Add(Thread,4910251,175,147)
{
Delay=300
FastStop=0
link(onExec,6962201:doEvent1,[])
}
Add(Hub,10901672,497,154)
{
OutCount=3
link(onEvent1,6614406:doOn,[(521,160)(521,139)])
link(onEvent2,15914761:doWork1,[(620,167)])
link(onEvent3,9010111:doFor,[])
}
Add(LED,6614406,532,119)
{
Left=260
Top=10
ColorOn=65280
ColorOff=32768
ColorBlick=65280
}
Add(HubEx,15914761,616,175)
{
link(onEvent,1559756:doProcessMessages,[])
}
Add(Hub,3835869,119,315)
{
OutCount=3
link(onEvent1,14120473:doChangeValue,[(147,321)(147,244)])
link(onEvent2,15271927:doEvent1,[])
link(onEvent3,5233333:doChangeValue,[(147,335)(147,440)])
}
Add(LED,5233333,217,434)
{
Left=340
Top=10
ColorOn=16776960
ColorOff=8421376
ColorBlick=16776960
}
Add(InfoTip,8429069,154,98)
{
Info=#50:Запускаем работу с ресурсом в параллельном потоке |
Width=155
Height=102
}
Add(InfoTip,1151692,154,399)
{
Info=#31:Делаем чего-то, никому не мешая|
Width=155
Height=81
}
Add(InfoTip,10690651,154,287)
{
Info=#52:Пытаемся запустить работу с ресурсом в другом потоке|0:|0:|0:|0:|48:Доступ откроется тогда, когда освободится ресурс|
Width=155
Height=109
}
Add(LED,14120473,217,238)
{
Left=380
Top=10
ColorOn=65535
ColorOff=32896
ColorBlick=65535
Value=0
}
Add(InfoTip,9292949,154,203)
{
Info=#31:Делаем чего-то, никому не мешая|
Width=155
Height=81
}
Add(InfoTip,11626540,483,91)
{
Info=#23:Тут работаем с ресурсом|
Width=239
Height=137
}
Add(SafeMode,14966444,385,154)
{
WaitMode=1
link(onSafeMode,10901672:doEvent1,[])
}
Add(Hub,15271927,231,322)
{
OutCount=3
link(onEvent1,10327193:doData,[])
link(onEvent2,882968:doOn,[(318,335)(318,251)])
link(onEvent3,1357669:doSafeMode,[])
}
Add(Switch,882968,595,231)
{
DataOn=Integer(1)
DataOff=Integer(0)
Point(doOn)
Point(State)
}
Add(DoData,10327193,679,322)
{
Data=String(Попытка доступа к ресурсу)
link(onEventData,6231889:doWork3,[(732,328)])
}
Add(IndexToChanel,11574106,595,273)
{
Point(Index)
link(onEvent2,8910723:doData,[])
link(Index,882968:State,[])
}
Add(LED,8001298,679,357)
{
Left=300
Top=10
ColorBlick=255
}
Add(DoData,8910723,679,280)
{
Data=String(Ресурс открыт для другого потока)
link(onEventData,6231889:doWork2,[])
}
Add(HubEx,6231889,728,280)
{
Angle=3
link(onEvent,13823805:doWork2,[(732,69)])
}
Add(DoData,7263705,266,126)
{
Data=String(Работает параллельный поток)
link(onEventData,13823805:doWork3,[(319,132)])
}
Add(Hub,6962201,224,147)
{
link(onEvent1,7263705:doData,[(252,153)(252,132)])
link(onEvent2,14966444:doSafeMode,[])
}
Add(HubEx,13823805,315,63)
{
Angle=2
link(onEvent,14017285:doCaption,[(58,69)(58,139)])
}
Add(Hub,3917072,560,371)
{
link(onEvent1,8001298:doOn,[])
link(onEvent2,8922034:doTimer,[])
}
Add(Timer,8922034,595,378)
{
Interval=500
Enable=1
AutoStop=1
link(onTimer,8001298:doOff,[(653,384)(653,370)])
}
Add(Edit,3456976,595,420)
{
Left=110
Top=13
Width=95
Text=""
}
Add(DoData,7394771,532,420)
{
link(onEventData,3456976:doText,[])
link(Data,9010111:Position,[])
}
Add(SafeMode,1357669,385,336)
{
WaitMode=1
link(onSafeMode,13329192:doEvent1,[])
}
Add(Hub,13329192,490,336)
{
OutCount=3
link(onEvent1,11574106:doEvent,[(521,342)(521,279)])
link(onEvent2,7394771:doData,[(521,349)(521,426)])
link(onEvent3,16319066:doNext,[(514,356)(514,475)])
}
Add(Hub,7461548,119,147)
{
link(onEvent1,4910251:doStart,[])
link(onEvent2,16126828:doStart,[(143,160)(143,191)(58,191)(58,321)])
}
Add(Thread,16126828,70,315)
{
link(onExec,3835869:doEvent1,[])
link(onSyncExec,3917072:doEvent1,[(113,328)(113,377)])
}
Add(Edit,5685232,595,469)
{
Left=210
Top=13
Width=45
Text="0"
}
Add(Counter,16319066,532,469)
{
link(onNext,5685232:doText,[])
}
Add(InfoTip,16687612,336,280)
{
Info=#31:Дожидаемся освобождения ресурса|
Width=141
Height=137
}

карма: 0

0
Разработчик
Ответов: 26061
Рейтинг: 2120
#19: 2019-01-08 02:46:56 ЛС | профиль | цитата
Neo писал(а):
Так получается что выводить в оконные элементы можно из параллельного потока прямо по onExec?

Можно, но не нужно. Работает это крайне нестабильно.
Neo писал(а):
Да и читать данные из того же Text тоже можно в параллельном потоке?

Это можно, команды на перерисовку контрола, в таком случае, не возникает.
Neo писал(а):
Но разбираясь с примером выше, обнаружил что идет работа вообще без мьютексов

Если в моем примере, то там их отродясь и не было за ненадобностью, как, кстати, и вот этого там тоже нет
Neo писал(а):
получается что выводить в оконные элементы можно из параллельного потока прямо по onExec?


Neo писал(а):
Штатную схему прикрепляю

Ха, тебя сбивает с толку управление из потока напрямую контролами, но так это просто пример. В основных, стабильных схемах так лучше не делать.

Редактировалось 4 раз(а), последний 2019-01-08 03:13:27
карма: 22

0
Ответов: 704
Рейтинг: 7
#20: 2019-01-08 12:50:44 ЛС | профиль | цитата
nesco, благодарю! Супер-понятно и подробно.
карма: 0

0
Ответов: 4612
Рейтинг: 746
#21: 2019-01-08 13:15:34 ЛС | профиль | цитата
nesco писал(а):
внутренним методом Sender.Synchronize(SyncExec), а сам продолжает выполняться дальше ... те параллельный поток не ждет окончания события главного потока
Ага, щас. Если бы TThread.Synchronize() реализовывался через PostMessage() - то так и было бы. А он использует SendMessage().

Редактировалось 1 раз(а), последний 2019-01-08 13:19:03
карма: 26

0
Разработчик
Ответов: 26061
Рейтинг: 2120
#22: 2019-01-08 14:54:49 ЛС | профиль | цитата
Netspirit писал(а):
А он использует SendMessage()

Елки, а ведь точно. Тогда поток повиснет на время работы обработчика главным потоком. Вот почему поток работает с интерфейсным и элементами. Какие у тебя по этому вопросу мысли?

Редактировалось 1 раз(а), последний 2019-01-08 17:09:34
карма: 22

0
Ответов: 4612
Рейтинг: 746
#23: 2019-01-08 14:59:32 ЛС | профиль | цитата
nesco писал(а):
Какие у тебя по этому вопросу мысли?
Хм, так и задумано: onExec исполняется в параллельном потоке, onSyncExec - в главном, "для доступа к интерфейсным компонентам" (компонент Synchronize делает то же и избавляет от необходимости этого метода). Без компонента Synchronize другой возможности безопасно работать с интерфейсными элементами из параллельного потока не было.

Редактировалось 4 раз(а), последний 2019-01-08 15:03:53
карма: 26

0
Разработчик
Ответов: 26061
Рейтинг: 2120
#24: 2019-01-08 17:23:45 ЛС | профиль | цитата
Получается так, что onSyncExec и Synchronize вызывают обработчик главного потока и ждут завершения его работы. Но на время обработки сообщений главным потоком параллельный поток останавливается и ничего не может делать параллельно. Вот мне тут не понятен один момент -- а что, если интерфейсный элемент задержится с обработкой сообщений, а лимит времени остановки параллельного потока закончится? Или этот лимит бесконечен? Ты по этому вопросу, случаем, ничего не знаешь?
карма: 22

0
Ответов: 16884
Рейтинг: 1239
#25: 2019-01-08 21:08:17 ЛС | профиль | цитата
Видит бог - долго терпел.
Neo писал(а):
Столкнулся с тем, что таблица строк на 7 строк по 38 колонок каждая ощутимо тормозит главный поток при полной перерисовке через замену строк.
Значит программа глазами следит за таблицей!
Оказывается вроде нет.
Neo писал(а):
Просто каждые 3 секунды данные в таблице полностью обновляются и к таблице имеют доступ через матрицу строк или массив строк другие участки схемы
Следит, но другими участками схемы.
Neo, вопросы:
1. Зачем другим участкам схемы визуальная таблица строк?
2. Кому нужна 3-х секундная моргалка на экране?

Редактировалось 2 раз(а), последний 2019-01-08 21:14:43
карма: 25
Немного терпения! Дежурный экстрасенс скоро свяжется с Вами!
0
Ответов: 704
Рейтинг: 7
#26: 2019-01-08 23:28:55 ЛС | профиль | цитата
Tad, создавал программу давно когда еще трава была зеленее. Сейчас перестраиваю все под новый контроллер и под новые принципы. Таблица осталась изначально как отображение текущего положения дел. Иногда нужно ее посмотреть. Хотя сейчас планирую данные переносить в невизуальную матрицу строк и массив строк - так ведь надежнее? Но в таблице так удобно было с ними работать и наблюдать. Придется периодически перечитывать массив в таблицу для наблюдения. А вот сама таблица почему-то долго перерисовывается на некоторых вин10, пока не понял в чем дело.
карма: 0

0
Ответов: 16884
Рейтинг: 1239
#27: 2019-01-08 23:38:54 ЛС | профиль | цитата
Neo, вин10 32,64 ? Памяти сколько ?
карма: 25
Немного терпения! Дежурный экстрасенс скоро свяжется с Вами!
0
Ответов: 704
Рейтинг: 7
#28: 2019-01-09 00:53:19 ЛС | профиль | цитата
Х64, памяти 8ГБ. По удаленке единственное, проверяю. Но на моем ноуте тоже х64 есть мелкие подвисания главного потока из-за талицы. В Вашем примере стабильно 10 событий/сек, а у меня та же схема 8-12 плавает если ее двигать активно. При этом ВинXPх86 с 2ГБ памяти даже намека на подвисания не дает и по удаленке. Пробовал на 10 отключать эффекты визуальные - ничего не меняет.

Редактировалось 4 раз(а), последний 2019-01-09 00:57:36
карма: 0

0
Разработчик
Ответов: 26061
Рейтинг: 2120
#29: 2019-01-09 02:41:20 ЛС | профиль | цитата
Neo писал(а):
Пробовал на 10 отключать эффекты визуальные - ничего не меняет.

Попробуй для проверки принудительно включить высокий приоритет графики для своего приложения -- Win+I -> Игры -> Игровой режим -> Настройки Графики. У меня и для HiAsm включен этот режим.

Редактировалось 2 раз(а), последний 2019-01-09 02:43:17
карма: 22

0
Ответов: 4612
Рейтинг: 746
#30: 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).

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

1
Голосовали:nesco
Сообщение
...
Прикрепленные файлы
(файлы не залиты)