Сейчас на форуме: tyns777 (+5 невидимых)

 eXeL@B —› Программирование —› Многопоточность в Delphi
Посл.ответ Сообщение


Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 05 апреля 2011 01:02 · Поправил: Isaev
· Личное сообщение · #1

Решил на досуге поковыряться с потоками...
На текущий момент нарушена синхронизация вывода, что вполне логично...
Тут немного встрял. Каким образом её наладить?
1. В процедуре, что передаётся в Synchronize организовываются только изменение переменных по минимуму или туда можно влепить весомый кусок кода? Полностью вывод например в данный момент в Memo потом будет в файл...
2. Как в неё передавать значения? Если в файл нужно кидать массив например (или просто вывести строку, как сейчас)
3. Как добиться, чтобы нумерация строк не нарушалась?

Запись в файл нужно наверное вообще в критическую секцию помещать?

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: 10.0 (новичок), 44thx
Активность: 0.010
Статус: Участник

Создано: 05 апреля 2011 23:10
· Личное сообщение · #2

1. можно впринципе что угодно туда влепить, Synchronize вобщем-то нужен только для работы с главным потоком, т.е фактически с gui
2. собственно через поля потока
3. я так понял нумерация строк зависит от порядка выполнения потоков, их конечно можно заставить выполняться последовательно, через любые примитивы синхронизации, в т.ч и крит. секции, но в чем тогда вообще смысл _последовательного_ выполнения потоков? если нужно просто отсортировать строки в нужной последовательности, то это можно сделать и после завершения всех потоков..

все AFAIK&AFAIR




Ранг: 793.4 (! !), 568thx
Активность: 0.740
Статус: Участник
Шаман

Создано: 05 апреля 2011 23:27
· Личное сообщение · #3

A V всё правильно сказал.

По исходнику. Всё неправильно. Обращение к VCL из потока только через Synchronize. Параметры передаются через поля потока. Обращение к глобальным переменным только через критические секции, блокировки,.... Если не уверен в чем то, то представь как 2 потока одновременно с этим работают. Вот пример:

Procedure TMyThread.ThreadExit(Sender:TObject);
Begin
Dec(Alive);
End;

Представь, что Alive = 10. Dec(Alive) выглядит как
mov eax, [mem]
dec eax
mov [mem], eax

если все 10 потоков одновременно выполнят первую строчку, то последний поток запишет в Alive 9 хотя ни одного потока уже не будет.

Synchronize тормозит вызывающий поток и выполняет метод в контексте главного потока, поэтому если нужно много писать в лог, то могу предложить следующий трюк:

Динамически создаем строку. Копируем туда нужные данные. Засылаем указатель на строку главному окну функцией PostMessage. В главном окне в обработчике этого сообщения читаем строку и освобождаем память. Так поток не будет прерываться, а лог будет сваливаться стеком на главный поток VCL, проверено на одном ресурсоемком проекте со 150 одновременно работающими и срущими в лог потоками.

-----
Yann Tiersen best and do not fuck





Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 06 апреля 2011 01:14 · Поправил: Isaev
· Личное сообщение · #4

PE_Kill пишет:
Обращение к глобальным переменным только через критические секции, блокировки,...

Да, да... этот пример был для 1 потока, потому и не была учтена эта проблема.
PE_Kill пишет:
Synchronize тормозит вызывающий поток

я потому и задумался... если каждый поток будет на долго тормозиться, смысл от них
PE_Kill пишет:
могу предложить следующий трюк

Интересное решение, сделаю )

A V пишет:
я так понял нумерация строк зависит от порядка выполнения потоков

тут да, потому и путанка... в идеале не должна зависеть, появляется результат - прирост счётчика
как раз "трюк" PE_Kill'а это решит

PE_Kill пишет:
По исходнику. Всё неправильно.

Надо было с чего-то начать
Сейчас нашёл приятную статью по теме: --> Link <--
Изучу, будет вторая попытка...

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh





Ранг: 324.3 (мудрец), 222thx
Активность: 0.480.37
Статус: Участник

Создано: 06 апреля 2011 03:23 · Поправил: DenCoder
· Личное сообщение · #5

Можно LOCK dec Alive. (опкод F0 FE 0D xx xx xx xx, если размер переменной 1 байт, F0 FF 0D xx xx xx xx, если 4, 66 F0 FF 0D xx xx xx xx, если 2) То есть в простых случаях, применяя ассемблерные вставки с префиксом LOCK, отпадает необходимость думать о синхронизации.

По опыту - Дельфи достаточно крив в плане синхронизации потоков, если пользоваться стандартными компонентами, теми которые предлагает борланд. В частности, класс TSemaphore представляет собой вовсе не объект "семафор". WinAPI здесь решает много проблем. А ассемблерные вставки с блокировкой шины, если такое уместно, оптимизируют! Поэтому давно не пользуюсь Дельфи.

-----
IZ.RU





Ранг: 793.4 (! !), 568thx
Активность: 0.740
Статус: Участник
Шаман

Создано: 06 апреля 2011 10:12
· Личное сообщение · #6

DenCoder пишет:
По опыту - Дельфи достаточно крив в плане синхронизации потоков, если пользоваться стандартными компонентами, теми которые предлагает борланд.

По опыту, если писать в VCL приложении потоки на API, то имеем редкие и странные баги, которые можно годами отлавливать. Единственное с чем иногда приходится ковыряться это создание объектов внутри потоков, почему то некоторые работают корректно только если их создавать в контексте основного потока, приходится тогда делать синхронизацию для Create.

-----
Yann Tiersen best and do not fuck




Ранг: 88.3 (постоянный), 3thx
Активность: 0.040
Статус: Участник

Создано: 06 апреля 2011 11:50
· Личное сообщение · #7

Так из опыта... Использовал EnterCriticalSection/LeaveCriticalSection для выполнения участка кода в многопотоковом приложении, в делфи, причем внутри этого кода было и создание классов, и динамические массивы, и АПИ, почти все что угодно, и никогда никаких проблем не было...

Зато были проблемы писания данных в файл в таком коде (скорее всего такая же проблема как и тут...), пишешь в один файл из двух потоков, в резальтате в файле непонятно что, данные из двух потоков перекрываются..
Как вообще правильно писать в файл из нескольких потоков?

PS: операнды LOCK можно не использовать прямо в коде, есть же АПИ

function InterlockedIncrement(var Addend: Integer): Integer; stdcall;
function InterlockedDecrement(var Addend: Integer): Integer; stdcall;
function InterlockedExchange(var Target: Integer; Value: Integer): Integer; stdcall;
function InterlockedCompareExchange(var Destination: Longint; Exchange: Longint; Comperand: Longint): Longint stdcall;
function InterlockedExchangeAdd(Addend: PLongint; Value: Longint): Longint stdcall; overload;
function InterlockedExchangeAdd(var Addend: Longint; Value: Longint): Longint stdcall; overload;




Ранг: 793.4 (! !), 568thx
Активность: 0.740
Статус: Участник
Шаман

Создано: 06 апреля 2011 12:00
· Личное сообщение · #8

Enigma пишет:
и создание классов, и динамические массивы, и АПИ, почти все что угодно, и никогда никаких проблем не было

Ключевое слово здесь: VCL - Visual Component Library. С обычными классами такой проблемы нет, косяк кроется в потоконебезопасной организации кеша VCL. Простой пример - создай форму с ListView в режиме Report и потоков 50 на апи, которые будут с ним активно работать, добавлять/удалять/изменять данные и юзать поле Data, максимум через час вылезает отсос виолейшн.

Enigma пишет:
Как вообще правильно писать в файл из нескольких потоков?

Перекрываются в смысле накладываются друг на друга? Если структура лога не важна я использую WinApi и флешу буферы после записи. Иначе делаю на каждый поток по логу и если нужен общий, то объединяю их потом.

-----
Yann Tiersen best and do not fuck


| Сообщение посчитали полезным: Enigma

Ранг: 88.3 (постоянный), 3thx
Активность: 0.040
Статус: Участник

Создано: 06 апреля 2011 12:19
· Личное сообщение · #9

PE_Kill пишет:
максимум через час вылезает отсос виолейшн.


Ясно! Кстати, видел в кое каком приложении (многие наверное тоже это видели...), и обеспечения нормальной работы многопоточности, используеются все 3 метода, EnterCriticalSection/LeaveCriticalSection + InterlockedExchange + CreateSemaphore/ReleaseSemaphore. Возможно имеет смысл повзаимствовать эту идеи и проверить ее на VCL.

PE_Kill пишет:
Перекрываются в смысле накладываются друг на друга? Если структура лога не важна я использую WinApi и флешу буферы после записи. Иначе делаю на каждый поток по логу и если нужен общий, то объединяю их потом.

Ага, просто покдадываются.. Пишется пол буфера из одного треда, потом пол буфера из второго, потом дописываются остатки...
Флешишь буферы это FlushFileBuffers...? Хм.. не знал такой АПИ, спасибо! Можно наверное еще файл открывать через ZwCreateFile/ZwOpenFile с параметром FILE_SEQUENTIAL_ONLY, надо протестировать..




Ранг: 793.4 (! !), 568thx
Активность: 0.740
Статус: Участник
Шаман

Создано: 06 апреля 2011 13:09
· Личное сообщение · #10

Enigma пишет:
Возможно имеет смысл повзаимствовать эту идеи и проверить ее на VCL.

Не поможет. Т.к. это лишь разграничит доступ между потоками, а главный то поток ничего не знает о других. Поэтому борланд и сделал такой костыль (Synchronize), как в свое время и Application.ProcessMessages.

Enigma пишет:
Флешишь буферы это FlushFileBuffers...?

Ага, а то тоже бывало, что буферы накладывались.

-----
Yann Tiersen best and do not fuck




Ранг: 54.9 (постоянный)
Активность: 0.020
Статус: Участник

Создано: 07 апреля 2011 18:46
· Личное сообщение · #11

Запись в файл из нескольких потоков будет тормозить работу, хотя если данных не много, то и не заметно будет. Критические секции тормозят все потоки до выхода из неё, можно использовать мьютекс для синхронизации записи в файл. Все это проверял на многопоточном приложении, с кучей вспомогательных exe файлов пишущих все в один файл, итог как описано выше все приложения пишут в свой файл, потом все сливается в один. Если использовать winapi createfile, FileWrite для записи в один файл, то можно не синхронизировать, ос делает это сама, проверял.
По поводу многопоточности в делфи, есть весьма продвинутые исходники http://otl.17slon.com/download.htm можно скачать архив, но лучше по svn. В коммерческой разработке использовались весьма успешно.




Ранг: 793.4 (! !), 568thx
Активность: 0.740
Статус: Участник
Шаман

Создано: 08 апреля 2011 07:24
· Личное сообщение · #12

xShadow пишет:
Запись в файл из нескольких потоков будет тормозить работу, хотя если данных не много, то и не заметно будет.

Если использовать PostMessage как я выше писал, то пишет в файл только один поток, он как бы является менеджером логов. Поэтому тормозов нет, хотя винчестер конечно насилуется.

-----
Yann Tiersen best and do not fuck




Ранг: 88.3 (постоянный), 3thx
Активность: 0.040
Статус: Участник

Создано: 08 апреля 2011 14:33
· Личное сообщение · #13

Да, попробовал у себя использовать FlushFileBuffers, все стало рабоать наверное раз в 100 медленнее... хотя теперь без ошибок. Данных у меня как раз пишется много..




Ранг: 324.3 (мудрец), 222thx
Активность: 0.480.37
Статус: Участник

Создано: 08 апреля 2011 15:58
· Личное сообщение · #14

FlushFileBuffers - только хард мучать. Я нашел эту функу полезной только когда приложение бсодит и надо лог о последних действиях записать. Без нее вроде виндовый кеш нормально рулит...

-----
IZ.RU





Ранг: 793.4 (! !), 568thx
Активность: 0.740
Статус: Участник
Шаман

Создано: 08 апреля 2011 16:31
· Личное сообщение · #15

DenCoder пишет:
Без нее вроде виндовый кеш нормально рулит...

С потоками не нормально.

-----
Yann Tiersen best and do not fuck




Ранг: 419.0 (мудрец), 647thx
Активность: 0.460.51
Статус: Участник
"Тибериумный реверсинг"

Создано: 08 апреля 2011 19:29
· Личное сообщение · #16

Писать в файл несколькими потоками на мой взгляд вообще не самый лучший вариант их применения. Один вполне справится. Понимаю, если нужно например процессы анализировать на предмет нужной сигнатуры.
Самое главное грамотно распорядится их количеством и назначением. А синхронизация - вышеупомянутый LOCK и через глобальные переменные(мне в VC++ этого хватало). Кстати сама мелкософт говорит что мьютексы, семафоры это долго все, критические секции самые быстрые(в rsdn статья была за синхронизацию)




Ранг: 756.3 (! !), 113thx
Активность: 0.610.05
Статус: Участник
Student

Создано: 08 апреля 2011 20:00
· Личное сообщение · #17

Enigma пишет:
попробовал у себя использовать FlushFileBuffers, все стало рабоать наверное раз в 100 медленнее...

Ну так конечно...
А если использовать 1 раз в конце не то же самое будет?

-----
z+Dw7uLu5+jqLCDq7vLu8PvpIPHs7uMh




Ранг: 65.3 (постоянный), 10thx
Активность: 0.020
Статус: Участник

Создано: 10 апреля 2011 11:14
· Личное сообщение · #18

Isaev
Что значит, в конце? Либо сбрасывать буфера после каждой записи каждым потоком, и тогда запись медленная, но последовательная. Или винда сбрасывает буферы сама, когда файл закрывается, или когда буферы переполняются. И тут быстро, но мешанина.
А по сабжу - правильно PE_Kill говорит, я думаю. Один поток и postmessage. Ну или нафигачить еще один поток, сделать лист строк, которые писать, лочить его в потоках, кидать в него строку, разлочивать. И в новом потоке лочить, клонировать, очищать, анлочить, писать на хард клона.


 eXeL@B —› Программирование —› Многопоточность в Delphi
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати