Имя: Пароль:
IT
 
Многопоточность в 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) ++