|
Многопоточность в Delphi | ☑ | ||
---|---|---|---|---|
0
H A D G E H O G s
07.03.23
✎
23:14
|
Дня доброго.
Идут годы и в Delphi запилили многопоточность прям вот как Java и даже лучше: Есть простой советский копеечный код: procedure TForm2.Button1Click(Sender: TObject); var i, total: Integer; begin total := 8; TParallel.For(1, total, procedure(i: Integer) var localstr: String; Data: String; index: Integer; begin Data := 'test1 test2'; index := 0; while true do begin index := index + 1; localstr := copy(Data, index, 1); if index > 2 then index := 0; end; end); end; который заработает, если вы добавите System.Threading в uses. Чисто с первого взгляда, он даст прикурить вашему многоядерному процу, но нет. Пока мы не уберем строку кода localstr := copy(Data, index, 1); он будет работать на одном ядре. Потоки будут ждать друг друга на блокировке, которая возникает при очистке строковой переменной примерно тут: function _UStrClr(var S): Pointer; ..... if AtomicDecrement(P.refCnt) = 0 then FreeMem(P); AtomicDecrement() накладывает блокировку, дабы удостовериться, что счетчик ссылок на строку не будет одновременно уменьшен в разных потоках: https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.AtomicDecrement Но я абсолютно уверен, что мои строки изолированы внутри потока. Можно как то сказать TParallels, чтобы так не делал? |
|||
1
Garykom
гуру
07.03.23
✎
23:30
|
проблема функции copy() типа string в целом?
|
|||
2
Garykom
гуру
07.03.23
✎
23:30
|
(1) *или типа
|
|||
3
Garykom
гуру
07.03.23
✎
23:32
|
если код из этого js изврата переписать на классический дельфо-много-поточный тоже самое?
|
|||
4
H A D G E H O G s
07.03.23
✎
23:33
|
(2) Проблема в избыточной потокобезопасности строк, которая мне не нужна. Я могу попробовать уйти от этого, юзая низкоуровневое WinAPI, но хотелось бы удобных фич.
|
|||
5
H A D G E H O G s
07.03.23
✎
23:33
|
Не верю я, что нет выключателя.
|
|||
6
Garykom
гуру
07.03.23
✎
23:38
|
(5) Дельфи ваяли еще в древние времена так что может не быть
Имхо возьми уже более современную технологию Я на 200% уверен что нужное тебе на Go делается намного проще и будет работать быстрей |
|||
7
Garykom
гуру
07.03.23
✎
23:38
|
Задачу можешь описать?
|
|||
8
Garykom
гуру
07.03.23
✎
23:39
|
(6)+ а dll на Go можно уже к GUI на Delphi подрубать ))
|
|||
9
H A D G E H O G s
07.03.23
✎
23:42
|
(7) Интенсивная многопоточная работа со строками.
|
|||
10
Guk
07.03.23
✎
23:44
|
ветка ностальжи. последний проект на Delphi закончил в уже далеком 1997 году...
|
|||
11
Mort
07.03.23
✎
23:50
|
Дельфи это который паскаль от борланд с VCL? Много в своё время на Borland C++ сделал, да ностальжи. Правда совсем затух в одно время.
|
|||
12
vde69
07.03.23
✎
23:51
|
Сразу скажу, я не знаю как сделан TParallel.
Но потоки работают в разных адресных пространствах и переменные одного потока не должны влиять на другой. В твоем примере мне кажется не верным то, что ты определил функцию внутри класса и внутри этой функции определяешь переменную. Попробуй вместо переменных использовать типизированные указатели, а память под них выделять внутри локальной процедуры (то есть откажись от статического связывания внутри TParallel) |
|||
13
Garykom
гуру
07.03.23
✎
23:53
|
||||
14
Garykom
гуру
07.03.23
✎
23:54
|
(12) >внутри TParallel
тоже сказал про это |
|||
15
Garykom
гуру
07.03.23
✎
23:54
|
Но имхо выкинуть лисапед и заюзать https://github.com/NuclearAPK/go-techLog1C
|
|||
16
timurhv
07.03.23
✎
23:58
|
Delphi обошла меня стороной. Только не понимаю, почему пишут что в IDE Delphi добавили многопоточность. Это как сказать в EDT 1С добавили регулярные выражения, нет?
|
|||
17
timurhv
07.03.23
✎
23:58
|
Язык же Object Pascal
|
|||
18
vde69
07.03.23
✎
23:59
|
(15) в дельфи относительно не сложно создаются потоки средствами апи винды, там только 3 заморочки
1. следить за дискрипторами (что-бы не было утечки ресурсов) 2. совместное использование данных (вроде файла проекции в память), ну и тут могут быть проблеммы с антивирусами, они не любят подобное 3. управление (семафоры, и т.д.) |
|||
19
Chai Nic
07.03.23
✎
23:59
|
А какие проблемы винапишную многопоточность заюзать?
|
|||
20
Garykom
гуру
08.03.23
✎
00:00
|
(16) В Дельфи многопоточность была давным давно
Просто сахару отсыпали но кривовато |
|||
21
timurhv
08.03.23
✎
00:02
|
(17) Может путаю, 1 раз только на работе попросили скомпилировать dll для 7.7 с другим алгоритмом расчета, на этом все.
|
|||
22
ДедМорроз
08.03.23
✎
01:48
|
Создание потока средствами Win API - это не сложно,но даже в Си,используя кучу памяти мы напарываемся на взаимоблокировки.
Очень и очень сложно сделать так,чтобы у каждого потока была своя куча памяти,а даже если и сделать,то передача данных между ними - еще тот атоакцион. |
|||
23
ДедМорроз
08.03.23
✎
01:53
|
В windows все потоки создаются в одном виртуальном адресном пространстве,так что там проблем с доступом к памяти нету.
Что касается проецирования файла в память,то это используется,когда блок памяти нужно сделать общим для нескольких процессов,и антивирусы на такой блок жалуются только если у этой памяти пытаются установить признак выполнения - без него ни один антивирус не жалуется,так как это достаточно распространенный способ,в частности - для общения с системными библиотеками. |
|||
24
H A D G E H O G s
08.03.23
✎
01:59
|
Перепилил на WideString и многопоточность взлетела, потому что в нем нет счетчика ссылок.
Изначально запилил свою версию очистки строки, заменив AtomicDec(P.refCnt); на dec(P.refCnt); но она рушилась в многопоточном режиме: function StrClr(S:pointer): Pointer; var P: PStrRec; unkptr:pointer; begin if Pointer(S) <> nil then begin P := Pointer(PByte(S) - SizeOf(StrRec)); unkptr:=p; Pointer(S) := nil; if P.refCnt > 0 then begin dec(P.refCnt); if (P.refCnt) = 0 then FreeMem(unkptr,P.length*p.elemSize+SizeOf(StrRec)); end; end; Result := S; end; |
|||
25
H A D G E H O G s
08.03.23
✎
02:00
|
Сейчас еще гляну FastMM5, там обещают улучшения параллельности (в Delphi из коробки юзается FastMM4).
|
|||
26
Garykom
гуру
08.03.23
✎
02:49
|
(24) >Перепилил на WideString и многопоточность взлетела
поменять тип логичное решение но имхо хватит уже некроЯП использовать чем не нравится готовое решение (13)? взять и допилить под себя если уж так хочется техжурнал в mssql засовывать, хотя это такое себе решение эластик намного в этом лучше! |
|||
27
Кирпич
08.03.23
✎
07:55
|
(0) Можно не использовать строки. Тут надо как на Си колбасить, без подвыподвертов, если надо чтобы быстро работало.
|
|||
28
Кирпич
08.03.23
✎
08:37
|
В delphi еще threadvar есть (переменные, которые копируются для каждого потока). Может их использовать? Только они вроде сами не удаляются из памяти. Нужно потом руками localstr := ''
Но лучше свои буферы с указателями использовать, вместо строк. |
|||
29
Chai Nic
08.03.23
✎
09:25
|
(22) "Очень и очень сложно сделать так,чтобы у каждого потока была своя куча памяти"
В многопоточных процедурах использования кучи надо избегать. Лучше пользоваться динамическим выделением памяти из стека. Это функции alloca и подобные. |
|||
30
ДедМорроз
08.03.23
✎
09:33
|
Свой менеджер памяти и выделение одного блока под поток - это лучше,чем из стека
|
|||
31
Chai Nic
08.03.23
✎
09:40
|
(30) Но сложнее. С этим связываться надо если проект серьезный и много памяти выделять приходится. А для простенького алгоритма лучше в стеке брать.
|
|||
32
ДедМорроз
08.03.23
✎
12:06
|
CreateHeap что может быть проще ?
Амв серьезном проекте лучше вообще как можно меньше операций с памятью делать,так как они очень затратные. |
|||
33
H A D G E H O G s
08.03.23
✎
13:23
|
FastMM5 порешал все проблемы с блокировками.
|
|||
34
Chai Nic
08.03.23
✎
13:29
|
(32) "в серьезном проекте лучше вообще как можно меньше операций с памятью делать,так как они очень затратные"
Жаль, что разработчики платформы 1с об этом не знают) |
|||
35
NorthWind
08.03.23
✎
13:53
|
(19) Хлопотно. Даже VCLный TThread тоже достаточно хлопотно. Посмотрите (0), это же вообще красота, сервисного кода ровно одна строчка. Считай, вообще ничего делать не надо.
|
|||
36
NorthWind
08.03.23
✎
13:56
|
(6) дельфи ваяли очень давно, да. Но до сих пор гуй для БД мало на чем можно сделать столь же просто. Вернее сказать, я вообще не знаю на чем. Есть, конечно, всякие там формсы, но они неуниверсальны (только для оракла). А вот так как на дельфи... по-моему, так до сих пор ничего и не придумали.
|
|||
37
APXi
08.03.23
✎
14:03
|
(36) + Тоже так же считаю. Если нужно по быстрому набросать прогу с какой нибудь БД, то кроме делфей ничего особо не подходит.
|
|||
38
H A D G E H O G s
08.03.23
✎
14:06
|
(35) Да. Я вне потока отладил чтение файлов, парсинг через регулярки с помощью Lazarus ского TFRLE (встроенный в Дельфи TRegExpr Lazarusский TRegExp вываливались на переполнении стека на полугигабайтных файлах), запись в TMemTable, сериализацию в файл и BulkInsert в MSSQL, либо проброс TMemTable в MSSQL напрямую через TPV.
А потом я мальнькой конструкцией из (0) всю вышеперечисленную iobu заворачиваю в многопоток. |
|||
39
H A D G E H O G s
08.03.23
✎
14:11
|
(36) Да, гуй под БД у Дельфи мощен. Какой нибудь простейший DBGrid от FastRepport или AlphaControls даст прикурить всему, что шевелится по функционалу, кроме динсписка от 1С, пожалуй. Про производительность, конечно, вообще говорить не стоит.
|
|||
40
Chai Nic
08.03.23
✎
15:23
|
(35) Ну, с учетом блокировки "из коробки" на строковых функциях это не совсем то, о чем мы мечтали. То есть, как бы красота, делать ничего не надо - но оказывается что локальные переменные процедуры такими вовсе не являются и есть точки неявных блокировок. По нормальному, рантайм должен это обыгрывать правильно, если переменная объявляется в процедуре - то она должна быть потокобезопасной, независимо от её реализации.
|
|||
41
NorthWind
08.03.23
✎
15:26
|
(40) идеала не бывает, всегда есть нюансы.
|
|||
42
Chai Nic
08.03.23
✎
15:34
|
(41) Нет уж. Не получается идеально - пусть будет хотя бы привычно. А делать по новому но тоже криво - просто напрасно повышать энтропию вселенной.
|
|||
43
Кирпич
08.03.23
✎
16:18
|
(0) Может такое дерьмо со строками получается только когда через замыкание. Может если тупо в отдельной процедуре, по старинке, то и не будет этой блокировки.
|
|||
44
H A D G E H O G s
08.03.23
✎
16:30
|
(43) Нет. И через CreateThread тоже самое. Но я подключил FastMM5 и все заработало еще вчера. И вот тут мне сегодня написали про FastMM5
https://stackoverflow.com/questions/75666893/delphi-threads-locks-waits-on-strings-operation/75673491#75673491 |
|||
45
Chai Nic
08.03.23
✎
16:40
|
А интересно, как это в Лазарусе работает?
|
|||
46
vde69
08.03.23
✎
17:38
|
(44) ну собственно об этом я и говорил в (12), то есть тебе нужно все переменные (кроме стековых) создавать динамически для каждого потока отдельно.
А вообще я-бы сделал свой классс на основе TParallel где в конструкторе создавал нужные переменные ОБЪЕКТА - localstr, Data, index |
|||
47
Кирпич
08.03.23
✎
17:47
|
(46) Там дело только в реализации функции FreeMem. В delphi сделано тупо и криво, а в FastMM5 хорошо. И никакие фокусы не помогут.
|
|||
48
Кирпич
08.03.23
✎
17:49
|
(46) "А вообще я-бы сделал свой классс на основе TParallel где в конструкторе создавал нужные переменные ОБЪЕКТА - localstr, Data, index"
ересь полная |
|||
49
NorthWind
08.03.23
✎
18:14
|
(46) так это уже сделано столько лет сколько дельфя 32-битная существует (а может, и 16-битная). Наследуешься от TThread и все.
Но все дело в том, что тогда нужно заметно менять архитектуру приложения. А вариант в (0) настолько щадящий в этом смысле, насколько это вообще возможно. |
|||
50
H A D G E H O G s
08.03.23
✎
18:24
|
(46) Это все классно и круто, если бы я не использовал flre
https://github.com/BeRo1985/flre в котором 750 Кб кода по реализации своего компилятора и виртуальной машины обработки регулярок и которая работает с обычными строками. FastMM5 мне отлично подошел |
|||
51
H A D G E H O G s
08.03.23
✎
18:26
|
(47) ++
|
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |