Пишу программу для работы с почтовыми ящиками Вот процедура кнопки: Code: procedure TForm1.Button1Click(Sender: TObject); var i: integer; begin Button1.Enabled := False; s := 0; for i := 0 to strtoint(Edit2.text) - 1 do begin FindLetterT := TFindLetter.Create(False); FindLetterT.Priority := tpNormal; end; end; Вот кусок кода потока: Code: for s := 0 to Form1.Memo2.Lines.count - 1 do // Пока есть аккаунты begin Form1.Memo3.Lines.Add('Поток вошел'); acc := Form1.Memo2.Lines[s]; Вот что происходит в программе: По скрину видно, что в цикл вошло пять потоков, хотя должен был один (так как от S до Form1.Memo2.Lines.count - 1 один шаг). Почему так? Как исправить?
Использовать критические секции. var CS:TcriticalSection; CS.Enter; //это чтоб потоки не косячили code; CS.Leave;
Также не изменять GUI из дополнительного потока, могут появиться ошибки. Используй метод TThread.Synchronize.
Тогда есть смысл использовать вместо for while и в критическую секцию запихивать прибавление единицы к счетчику и присваивание аккаунта переменной. Я все правильно понял? Или что-то еще требуется поместить туда?
Это небезопасный код. Если есть другой участок, также работающий с проблемным компонентом, снова появится гонка. Нужно использовать TThread.Synchronize, как тут и советовали - он заблокирует всё VCL глобально. И, кроме того, нужно использовать try/finally, чтобы гарантированно выйти из критической секции в случае исключения
http://grabberz.com/showthread.php?t=24619 Годная статья по многопоточности в Delphi, во второй части статьи еще расписано очень подробно что почему и как.
Code: CS.Enter; //это чтоб потоки не косячили Поржал) Надо уж хотя бы так - Code: CS.Enter; //Ну ты это...не косячь!
Ознакомился. Отличная статья о том, как НЕ надо работать с потоками в delphi. Вранье. Эффективность потоков под windows заканчивается вместе с количеством процессорных ядер - дальше идет спад. Это при условии, что процессоры загружают процессор полностью. Но даже если потоки простаивают, остается оверхэд на: 1. Переключение контекстов 2. Мегарасход памяти 3. Под виндой еще голодают другие процессы, поэтому система начинает тормозить. Единственное, в чем преимущество потоков для сети - проще использовать, нежели что-то асинхроннное (с чем у делфи большие проблемы - нет нормальных библиотек/компонентов под это дело, и возможностей языка типа сопрограмм, акторов). Про синхронизацию в VCL - обычная TCriticalSection не пригодна в принципе. Дело в том, что любой компонент может внезапно потянуть за собой обработчики событий, или получить сообщение от UI-потока => появится вероятность разрушить данные. Поэтому, если разделяется что-то унаследованное от TObject, необходимо поток завернуть в TThread и вызывать процедуру через Synchronize. Поскольку эта блокировка глобальная, она должна быть короткой по времени - иначе потоки будут выполняться большую часть времени последовательно. В идеале, нужно сделать отдельные методы для изменения свойств компонентов и вызывать их через Synchronize. Про передачу сообщений не упомянуто. Между тем, это наиболее эффективно - создать пул потоков и очередь заданий на выполнение.
Как по мне так лучше в многопоточности не использовать графических компонентов, или минимально использовать. Сделать через масив и т.д.
Если правильно писать, к примеру для приостановки использовать сигнальные объекты ядра(критические секции - это не объекты ядра), то все будет норм, переключений никаких не будет. А иногда даже требуется это ваше переключение контекстов(в этом случае ставят Sleep(0), в моей практике такое было. "Мегарасхода" памяти тоже не будет, если руки не из жопы. Бред, не верно написано. Критическая секция дает монопольный доступ на выполнение отрезка кода, к VCL это никак не относится вообще. Synchronize добавляет выполнение метода или процедуры в очередь, которая выполняется и соответственно очищается, когда приложение переходит в режим простоя(все Windows сообщения обработаны) и вызываются в контексте главного потока. Получается, что все данные, относящиеся к главному потоку и выполняются в нем. А вообще читать можно без всяких Synchronize данные главного потока, но только в том случае, когда точно известно, что они изменяться не будут в главном потоке во время чтения в левом потоке. Проще говоря можно без всякой синхронизации читать, а вот писать нельзя все кроме простейших типов данных, у которых запись происходит за одну команду процессора, в случае со строками или классами будет печаль у памяти и рано или поздно программа рухнет.
Помимо синхронизации на уровне ядра, переключение еще будет вызываться после вызовов функций блокирующего I/O. Так что любой вызов socket/connect/getaddrinfo/send/recv приведет к смене потока. Ну будет это переключение заменено активным ожиданием освобождения критической секции. Сути не меняет - это процессорное время по прежнему будет тратиться неэффективно, чем если бы мы выбирали, на примере работы с сетью, все события через select. Потоки для распараллеливания I/O - это плохо масштабируемый костыль - уже доказано не раз. Примеры? Разве что добавить искусственно тормозов, для эстетики Любое переключение, как минимум, сбрасывает кэш - а это влияет на скорость. Вот именно, что синхронизируется участок кода, а не факт доступа к объекту. В главном потоке может сработать обработчик, который изменит состояние объекта и, вместе с потоком, вызовет гонку. А Synchronize в основе использует глобальный лок (Classes.ThreadLock), и потому через него могут синхронизироваться все потокобезопасные компоненты. И как это гарантировать? Разве что вывести уравнение для мироздания и доказать, что программист не забудет о такой "ловушке" через некоторое время и не добавит компоненту новый обработчик события (например, OnMouseOver), который изменит состояние визуального объекта. Но при наличии такого уравнения писать очередную говноутилиту уже не захочется - появятся более глобальные цели Поскольку топикстартера интересовала именно синхронизация изменения визуального компонента, я считаю synchronize наилучшим решением данного вопроса, а все остальное - для любителей говнокода.