Вверх ↑
Этот топик читают: Гость
Ответов: 4630
Рейтинг: 749
#16: 2021-02-12 17:54:25 ЛС | профиль | цитата
Возможны конфликты при использовании параллельных потоков, когда потоки модифицируют одни и те же данные без защиты от одновременного доступа.
На одном или другом компьютере отличается только вероятность появления ошибок. Например, многопоточные ошибки значительно реже проявляются на одноядерных процессорах. Также на вероятность их появления влияет производительность/загруженность системы (когда загружена другой работой, то твоей программе отводится меньше времени исполнения, соответственно, меньше вероятность конфликтов).

Там осталось ещё одно узкое место: после замены таймера в главной форме добавление фразы "Пробуем отправить запрос..." в Memo получается тоже из параллельного потока - все такие действия тоже надо пускать через Synchronize. Убери Synchronize от контейнера и поставь сразу перед Memo.doAdd - будет гуд.

Редактировалось 1 раз(а), последний 2021-02-12 17:59:54
карма: 26

1
Голосовали:MBO
Ответов: 39
Рейтинг: 0
#17: 2021-02-12 20:16:58 ЛС | профиль | цитата
Понятно, спасибо
карма: 0

0
Ответов: 39
Рейтинг: 0
#18: 2021-02-12 23:31:48 ЛС | профиль | цитата
Такие вопросы возникли

Можно ли в одном потоке запускать несколько отправок запросов?

Можно ли стопить поток чтобы обработать ответ?

Может как то можно переписать хттп клиент чтобы не париться с потоками?)) что бы он был как старый хттп_гет

На компиляторе делфи такой ошибки не возникало, она появилась на fpc32 может там проблема?

--- Добавлено в 2021-02-12 23:48:48

А, может быть, поможет действительно вызывать поток внутри контейнера (faststop=true) с отправкой запроса. А на выходе поставить synchronize?

Или может быть есть возможность сначала, при отправке поток запустить, а перед выходом из контейнера остановить?

А то у меня работа программы завязан на остановке главного таймера (как в ручную так и программно при возникновении ошибок для генерации нового запроса)

Редактировалось 2 раз(а), последний 2021-02-12 23:49:37
карма: 0

0
Ответов: 4630
Рейтинг: 749
#19: 2021-02-14 14:37:38 ЛС | профиль | цитата
MBO писал(а):
Можно ли в одном потоке запускать несколько отправок запросов?
Можно. Последовательно. Несколько HTTPClient, выполняющие запросы по результатам предыдущих.
MBO писал(а):
Можно ли стопить поток чтобы обработать ответ?
Ответы обрабатываются в том же потоке, где выполняется запрос. Зачем его останавливать, если он и так стоит пока ты обрабатываешь ответ (например, не начнет выполнять следующий запрос).
MBO писал(а):
Может как то можно переписать хттп клиент чтобы не париться с потоками?
Thread+HTTPClient полностью эквивалентны HTTP_Get с потоком внутри. Ты преполгаешь что смог бы "стопить поток, чтобы обработать ответ", если бы использовал HTTP_Get?
MBO писал(а):
На компиляторе делфи такой ошибки не возникало, она появилась на fpc32 может там проблема?
Ошибки параллельных потоков (например, незащищенный доступ к визуальным компонентам), насколько я заметил, проявляются на FPC с большей вероятностью. Но это не значит что на Delphi они перестают быть ошибками.
MBO писал(а):
А, может быть, поможет действительно вызывать поток внутри контейнера (faststop=true) с отправкой запроса. А на выходе поставить synchronize?
FastStop=True требует твоих таймеров, чтобы они постоянно запускали поток, который постоянно останавливается. То-есть, ты опять наступаешь на те же грабли с попыткой обработки результатов параллельного потока из других потоков. А отсюда - желание остановить параллельный поток, не дождавшись его завершения (таймер в другом потоке ведь ждать не будет). Так MMTimer или Thread(FastStop=False) заменяют необходимость постоянного перезапуска Thread.
MBO писал(а):
Или может быть есть возможность сначала, при отправке поток запустить, а перед выходом из контейнера остановить?
А зачем? Потому что на событии выхода из контейнера не получается опять запустить тот же поток, потому что он ещё не остановлен? Так MMTimer или Thread(FastStop=False) извне контейнера решают эту задачу. Достаточно на выходе из контейнера не запускать поток, а просто ничего не делать (оно и так запущено и в цикле выполняет то что надо). А по результатам обработки можно только остановить выполнение. Собственно, MMTimer или Thread(FastStop=False) можно и внутрь засунуть - эффект тот же.

MBO писал(а):
А то у меня работа программы завязан на остановке главного таймера (как в ручную так и программно при возникновении ошибок для генерации нового запроса)
Так MMTimer или Thread(FastStop=False) извне контейнера прекрасно останавливаются и запускаются по любым командам.

Редактировалось 3 раз(а), последний 2021-02-14 14:44:03
карма: 26

0
Ответов: 39
Рейтинг: 0
#20: 2021-02-15 19:22:32 ЛС | профиль | цитата
Еще заметил такой баг, что WinExec перестает открывать URL в браузере (хотя файлы открываются нормально)
карма: 0

0
Ответов: 39
Рейтинг: 0
#21: 2021-02-15 22:01:21 ЛС | профиль | цитата
И я заметил еще такую строку в теме с новым компилятором:

спойлер
Внимание: на данный момент существует утечка памяти при использовании параллельных потоков. Для исправления после установки компилятора надо открыть файл "HiAsm\compiler\FPC2\fpc.cfg", в строке 20 вместо "-O3" поставить "-O1", сохранить. После чего выполнить файлы
"HiAsm\compiler\FPC2\src\_make_ALL_A.bat"
"HiAsm\compiler\FPC2\src\_make_ALL_U.bat"

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

0
Ответов: 4630
Рейтинг: 749
#22: 2021-02-16 13:00:25 ЛС | профиль | цитата
MBO писал(а):
это должно помочь?
Помочь с чем? С проблемой утечки памяти на компиляторе FPC 3.2.0? Да, ничего другого менять не надо. Все проблемы твоей программы это не решает - это конкретный нюанс конкретно этой сборки компилятора.

Редактировалось 2 раз(а), последний 2021-02-16 13:04:32
карма: 26

0
Ответов: 39
Рейтинг: 0
#23: 2021-02-16 14:05:45 ЛС | профиль | цитата
Netspirit писал(а):
MBO писал(а):
это должно помочь?
Помочь с чем? С проблемой утечки памяти на компиляторе FPC 3.2.0? Да, ничего другого менять не надо. Все проблемы твоей программы это не решает - это конкретный нюанс конкретно этой сборки компилятора.

Редактировалось 2 раз(а), последний 2021-02-16 13:04:32

В общем, спасибо тебе за помощь
В итоге, я свел все к такой схеме отправки запроса:

Add(MultiElement,9318530,420,714)
{
@Color=11163135
}
BEGIN_SDK
Add(EditMulti,3634868,21,21)
{
EventCount=1
WorkCount=1
DataCount=5
VarCount=1
Width=930
Height=781
link(doWork1,12666540:doCompare,[(42,27)(42,538)])
link(Var1,12900553:Value,[(27,473)(888,473)])
}
Add(MultiElement,15921226,455,476)
{
link(Data1,3634868:Data2,[(461,248)(34,248)])
}
BEGIN_SDK
Add(EditMulti,14911298,21,21)
{
WorkCount=1
DataCount=1
VarCount=3
Width=783
Height=494
link(doWork1,2327038:doSplit,[(53,27)(53,146)])
link(Var1,1160941:Value,[(27,361)(594,361)])
link(Var2,2055145:Value,[(34,361)(671,361)])
link(Var3,10482298:Value,[(41,361)(727,361)])
}
Add(MultiStrPart,2327038,84,140)
{
Char="@"
Point(onNotFound)
link(onSplit,7271578:doEvent1,[(149,146)])
link(Str,15582719:Var2,[])
link(onNotFound,12142469:doEvent1,[(124,153)(124,265)])
}
Add(Memory,1160941,588,175)
{
}
Add(Memory,2055145,665,175)
{
}
Add(Memory,10482298,721,175)
{
}
Add(Hub,12142469,147,259)
{
OutCount=3
link(onEvent1,7517633:doData,[])
link(onEvent2,2055145:doClear,[(392,272)(392,188)])
link(onEvent3,10482298:doClear,[(420,279)(420,188)])
}
Add(DoData,7517633,182,259)
{
link(onEventData,14285636:doEvent1,[(380,265)(380,181)])
link(Data,15582719:Var3,[(188,103)])
}
Add(GetDataEx,15582719,84,98)
{
Angle=3
link(Data,14911298:Data1,[(27,103)])
}
Add(Hub,7271578,147,119)
{
link(onEvent1,12435606:doData,[])
link(onEvent2,12544637:doSplit,[(193,132)(193,167)])
}
Add(DoData,12435606,196,119)
{
link(onEventData,14285636:doEvent2,[(390,125)(390,188)])
link(Data,2327038:Part1,[(202,107)(146,107)(146,184)(90,184)])
}
Add(Hub,14285636,553,175)
{
InCount=2
link(onEvent1,1160941:doValue,[])
}
Add(MultiStrPart,12544637,224,161)
{
Char=":"
Point(onNotFound)
link(onSplit,10835903:doEvent1,[])
link(Str,2327038:Part2,[(230,149)(164,149)(164,184)(97,184)])
}
Add(Hub,10835903,266,161)
{
link(onEvent1,15683516:doData,[(287,167)(287,146)])
link(onEvent2,3122225:doData,[(290,174)(290,188)])
}
Add(DoData,15683516,294,140)
{
link(onEventData,2055145:doValue,[(496,146)(496,181)])
link(Data,12544637:Part1,[(300,128)(265,128)(265,205)(230,205)])
}
Add(DoData,3122225,301,182)
{
link(onEventData,10482298:doValue,[(527,188)(527,181)])
link(Data,12544637:Part2,[(307,170)(272,170)(272,205)(237,205)])
}
END_SDK
Add(HTTPClient,13486350,595,686)
{
Method=1
EncType=1
ConnectTimeout=15000
SendTimeout=5000
RecvTimeout=5000
Point(Proxy)
Point(ProxyUser)
Point(ProxyPass)
Point(onAuthProxy)
Point(doUserAgent)
Point(PostData)
Point(onStateChange)
Point(doCustomMethod)
link(URL,3634868:Data1,[(601,326)(27,326)])
link(FileName,7399373:Var3,[(608,89)])
link(onFinish,7205917:doEvent1,[])
link(Proxy,15921226:Var1,[(615,569)(461,569)])
link(ProxyUser,15921226:Var2,[(622,569)(468,569)])
link(ProxyPass,15921226:Var3,[(629,569)(475,569)])
link(PostData,6535530:Var2,[])
}
Add(Hub,9914947,140,532)
{
OutCount=6
link(onEvent1,1447053:doWork1,[(448,538)(448,482)])
link(onEvent2,7097920:doWork2,[(472,545)(472,335)])
link(onEvent3,15921226:doWork1,[(304,552)(304,482)])
link(onEvent4,3577732:doCompare,[(326,559)(326,524)])
link(onEvent5,10903385:doStart,[(234,566)(234,678)])
link(onEvent6,14884823:doTimer,[(158,573)(158,713)])
}
Add(DoData,12209347,581,483)
{
link(onEventData,843525:doEvent1,[(625,489)(625,538)])
link(Data,7176825:Value,[(587,457)(650,457)])
}
Add(If_else,3577732,497,518)
{
link(onTrue,12209347:doData,[(555,524)(555,489)])
link(onFalse,13679300:doData,[(555,531)(555,538)])
link(Op1,14767262:Var2,[])
link(Op2,12767415:Value,[(510,496)(531,496)])
}
Add(GetDataEx,14767262,497,427)
{
link(Data,3634868:Data3,[(503,270)(41,270)])
}
Add(DoData,13679300,581,532)
{
link(onEventData,843525:doEvent2,[(625,538)(625,545)])
link(Data,14767262:Var3,[(587,432)])
}
Add(Memory,12767415,525,441)
{
Default=String()
}
Add(Memory,7176825,644,399)
{
Default=String(Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0)
}
Add(Thread,10903385,315,672)
{
Delay=500
Point(doStopFlag)
link(onExec,12894950:doCompare,[])
}
Add(Hub,7205917,721,693)
{
OutCount=4
link(onEvent1,4998178:doValue,[])
link(onEvent2,14884823:doStop,[(740,706)(740,713)(236,713)(236,717)(149,717)(149,720)])
link(onEvent3,10386299:doEvent2,[(740,713)(740,580)(688,580)(688,503)])
}
Add(Memory,1032677,784,476)
{
Default=String(1)
}
Add(ChanelToIndex,1447053,742,476)
{
link(onIndex,1032677:doValue,[])
}
Add(If_else,12666540,63,532)
{
link(onTrue,9914947:doEvent1,[])
link(Op1,15281892:Var1,[(69,520)])
link(Op2,9146203:Var1,[(76,460)])
}
Add(Memory,3176811,112,357)
{
Default=String(1)
}
Add(Hub,10386299,700,490)
{
InCount=2
link(onEvent1,1447053:doWork2,[(728,496)(728,489)])
}
Add(GetDataEx,13492034,602,609)
{
link(Data,3634868:Data4,[(608,326)(48,326)])
}
Add(GetDataEx,9146203,112,455)
{
link(Data,3176811:Value,[])
}
Add(If_else,6565431,483,672)
{
link(onTrue,13486350:doLoadString,[(555,678)(555,692)])
link(onFalse,13486350:doLoadFile,[(555,685)(555,699)])
link(Op1,7399373:Var2,[(489,318)(55,318)])
link(Op2,14123805:Value,[(496,418)(398,418)])
}
Add(GetDataEx,7399373,49,84)
{
link(Data,11395326:Value,[(55,79)(90,79)])
}
Add(Memory,14123805,392,133)
{
}
Add(GetDataEx,15281892,784,515)
{
link(Data,1032677:Value,[])
}
Add(GetDataEx,6535530,630,647)
{
Angle=3
link(Data,13492034:Var2,[(608,652)])
}
Add(Synchronize,15480477,840,112)
{
link(onSync,12900553:doValue,[])
link(Data,4998178:Value,[(846,100)(766,100)(766,737)(783,737)])
}
Add(Memory,4998178,777,693)
{
link(onData,15480477:doSynchronize,[(729,699)(729,118)])
}
Add(Memory,12900553,882,112)
{
link(onData,3634868:onEvent1,[(932,118)(932,27)])
}
Add(If_else,12894950,371,672)
{
link(onTrue,7609344:doEvent1,[(411,678)(411,671)])
link(Op1,12618824:Var2,[(377,522)(853,522)])
link(Op2,9146203:Var2,[(384,568)(118,568)])
}
Add(Memory,16438650,847,315)
{
Default=Integer(0)
}
Add(ChanelToIndex,7097920,791,322)
{
link(onIndex,16438650:doValue,[(835,328)(835,321)])
}
Add(Hub,7609344,420,665)
{
link(onEvent1,7097920:doWork1,[(612,671)(612,328)])
link(onEvent2,6565431:doCompare,[])
}
Add(GetDataEx,12618824,847,364)
{
link(Data,16438650:Value,[])
}
Add(Timer,14884823,161,707)
{
Interval=25000
Enable=1
AutoStop=1
link(onTimer,10386299:doEvent1,[(447,713)(447,496)])
}
Add(Hub,843525,637,532)
{
InCount=2
OutCount=1
link(onEvent1,13486350:doUserAgent,[(663,538)(663,626)(583,626)(583,713)])
}
Add(Memory,11395326,84,42)
{
}
END_SDK


Грубо говоря - я сначала запускаю поток и ставлю блокировку от повторений. А когда я подготавливаю новые данные для отправки на сервер я снимаю блок, посылаю данные ХТТПКлиенту и ставлю блок заново

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

Программа уже 5+ часов бесперебойно пашет

Последний вопрос, может лучше стопить поток, если минут 5-10 на хттпКлиент не посылались запросы (грубо говоря, пользователь сам тыкнул кнопку паузы)?

Таких потоков у программы может быть 15-20 одновременно (это прям максимум) а в среднем 4-5
карма: 0

0
Ответов: 4630
Рейтинг: 749
#24: 2021-02-16 14:17:43 ЛС | профиль | цитата
1) Да, перезапускать часто поток нерационально, лучше ставить на паузу
2) Насколько понимаю, ты останавливаешь запросы, не останавливая поток - с помощью If_else. Благодаря Delay=500 это приемлемо. Ещё можно ставить на реальную паузу с помощью WaitObject+Events.
MBO писал(а):
Последний вопрос, может лучше стопить поток, если минут 5-10 на хттпКлиент не посылались запросы (грубо говоря, пользователь сам тыкнул кнопку паузы)?
В таком случае поток элементарно останавливается с помощью doStopFlag и возобновляется по doExec.

Редактировалось 1 раз(а), последний 2021-02-16 14:18:20
карма: 26

0
Ответов: 39
Рейтинг: 0
#25: 2021-02-16 14:38:46 ЛС | профиль | цитата
То есть, тормозить (doStopFlag) поток после нескольких минут неактивности (планирую поставить 15 минут) - это норм?
карма: 0

0
Ответов: 4630
Рейтинг: 749
#26: 2021-02-16 14:52:44 ЛС | профиль | цитата
Да. Когда отработает ветка onExec, компонент Thread проверит флаг остановки и прекратит генерировать onExec, корректно завершившись.
После следующего doExec поток будет запущен вновь и продолжит выдавать события onExec.

Редактировалось 1 раз(а), последний 2021-02-16 14:53:33
карма: 26

0
26
Сообщение
...
Прикрепленные файлы
(файлы не залиты)