![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Потоки. Нужна ли синхронизация? [D6, XP]
vint45 © (2008-06-17 12:42:00)Здраствуйте. Есть приложение, в котором выполняются несколько потоков. Каждый поток имеет свой локальный набор переменных. Требуется периодически считывать из главного потока значения некоторых локальных переменных этих потоков (сбор статданных, для последующего отображения статистики на экране).
Вопрос. Надо ли помещать в критическую секцию процесс чтения локальных переменных главным потоком из остальных потоков?
ps прочитал пару статей по потокам, но там в основном рассматриваются варианты, когда потоки пытаются изменять, а не читать общие переменные.
Игорь Шевченко (2008-06-19 15:52:00) [61]Тын-Дын © (19.06.08 15:50) [76]
Просьба на [75] ответить
Тын-Дын (2008-06-19 15:52:00) [62]
> Еще раз, медленно и печально.
Можно побыстрее и не так печально. Смысл от этого не изменится. и необходимость в синхронизации тоже.
Игорь, тебе-то уж об этом задумываться - нужно-не нужно... Даже неудобно как-то.
Тын-Дын (2008-06-19 15:50:00) [63]
> MsGuns ©
> Игорь Шевченко ©
Для демонстрации схематичный пример потока.
type
TCopier=class(TThread)
private
FSize: int64; //Размер всех скопированных файлов
LastFileSize: int64; //Размер последнего скопированного файла
LastFileName: String; //Имя последнего скопированного файла
procedure Copy; //Процедура копирования
protected
procedure Execute; override;
Данные (Например, Private-Fields)из нескольких запущенных потоков должны попадать в основной поток и отображаться, затем сохраняться в журнал (основной поток).
Без синхронизации всякие размышления об этом бесмысленны.
Игорь Шевченко (2008-06-19 15:49:00) [64]Тын-Дын © (19.06.08 15:41) [74]
> Требуется не кошерность, а тривиальное - неразрушенные данные.
Еще раз, медленно и печально. Допустим, есть несколько потоков, занимающихся закачкой файлов. Для каждого файла известен его полный размер и размер текущего закаченного содержимого. Каждый файл качается своим потоком, соотв. оба размера являются локальными переменными этого потока. Еще один поток (главный, неглавный, неважно) занимается отображением прогресса скачивания файлов (Napster или FlashGet или eMule или Torrent видел ? - так вот у бабочек то же самое).
Соответственно, этот поток опрашивает локальные переменные (размер скачанной части) каждого потока и красиво выводит в Progress bar, можно даже с градиентом.
Локальные пременные могут быть длиной либо 32 бита, либо 64.
Какие тут могут быть разрушенные данные ?
Тын-Дын (2008-06-19 15:41:00) [65]
> Мы не в синагоге, нам кошерность не требуется, а требуется
> решение задачи. Каковая задача до сих в пор в понятном виде
> не озвучена.
Требуется не кошерность, а тривиальное - неразрушенные данные.
Без синхронизации (любым способом) этоневозможно. За исключением записи и чтения переменных размером в 1 байт.
Игорь Шевченко (2008-06-19 15:30:00) [66]Тын-Дын © (19.06.08 14:36) [70]
> Не супер-пупер, а обычная синхронизация требуется.
Нахрен не сдалась.
Мы не в синагоге, нам кошерность не требуется, а требуется решение задачи. Каковая задача до сих в пор в понятном виде не озвучена.
DVM (2008-06-19 15:18:00) [67]
> vint45 (19.06.08 15:14) [71]
> Соответственно чтение и запись одновременно выполняться
> не могут.
Так это и требуется. Иначе бардак будет.
vint45 (2008-06-19 15:14:00) [68]
> DVM © (19.06.08 12:55) [68]
В твоем примере чтение и запись свойства происходит через одну и ту же КС. Соответственно чтение и запись одновременно выполняться не могут. Т.е. если основной поток будет пытаться считать значение свойства, то доп. поток будет ожидать записи нового значения и наоборот, т.о. коллизий не возникнет.
А меня клинило, думал что одну и ту же КС можно вызвать внутри только одной процедуры.
> Тын-Дын ©
Мне важно выполнять корректное присвоение переменным основного потока. Каким образом это лучше сделать (получать данные основным потоком или доп. потоками), я пока не знаю. Хотя DVM привел, на мой взгляд, хороший пример.
Тын-Дын (2008-06-19 14:36:00) [69]
> Игорь Шевченко © (19.06.08 09:40) [64]
> MsGuns © (19.06.08 09:16) [63]
>
> Серега, смотри сам - если у тебя, допустим, N потоков занимаются,
> ну скажем закачкой файлов и в "локальных переменных" хранят
> прогресс этой самой закачки, а главный поток натурально
> этот прогресс в красивом виде отображает. Неужели в этом
> случае требуется супер-пупер синхронизация с критическими
> или некритическими секциями ?
Не супер-пупер, а обычная синхронизация требуется.
> MsGuns © (19.06.08 14:31) [69]
> >Игорь Шевченко © (19.06.08 09:40) [64]
>
> Подходит схема "опроса", т.е. КС и снхронизация не обязательны
> (и даже могут быть вредны, ибо тормозят)
Синхронизация обязательна. Забудь при работе с несколькими потоками о слове "Необязательно". Необязательно - это настолько редкий случай, что о нем не имеет смысла и говорить.
> vint45 (19.06.08 12:34) [65]
> 2. Главная задача состоит в корректном (синхронизированном)
> копировании значений переменных доп. потоков к основному
> потоку приложения (передача статданных в основной поток
> для последующего отображения в форме главного потока).
> Копирование значений переменных абы как, без синхронизации,
> или согласованности (не знаю как правильнее сказать, т.
> е. без проверки на то, что будут скопированы реальные значения
> переменных) считаю неправильным.
Так тебе получать данные нужно по инициативе основного потока, или все же передавать информацию из доп. потоков?
От этого зависит подход к решению задачи.
MsGuns (2008-06-19 14:31:00) [70]>Игорь Шевченко © (19.06.08 09:40) [64]
Подходит схема "опроса", т.е. КС и снхронизация не обязательны (и даже могут быть вредны, ибо тормозят)
DVM (2008-06-19 12:55:00) [71]
> vint45
Я не понимаю, в чем проблема сделать так, чтобы внутри метода Execute обращение к переменным было реализовано через свойства, защищенные КС.
interface
uses
Classes, SyncObjs;
type
TTestThread = class(TThread)
private
FParam: integer;
FCriticalSection: TCriticalSection;
procedure SetParam(AParam: integer);
function GetParam: integer;
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: boolean);
destructor Destroy; override;
prorerty Param: integer read GetParam write SetParam;
end;
implementation
constructor TTestThread.Create(CreateSuspended: boolean);
begin
FCriticalSection := TCriticalSection.Create;
inherited Create(CreateSuspended);
end;
destructor TTestThread.Destroy;
begin
FCriticalSection.Free;
inherited Destroy;
end;
procedure TTestThread.SetParam(AParam: integer);
begin
FCriticalSection.Enter;
try
FParam := AParam;
finally
FCriticalSection.Leave;
end;
end;
function TTestThread.GetParam: integer;
begin
FCriticalSection.Enter;
try
result := FParam;
finally
FCriticalSection.Leave;
end;
end;
procedure TTestThread.Execute;
begin
Param := 0;
while not Terminated do
begin
Param := Param + 1;
sleep(100);
end;
end;
DVM (2008-06-19 12:45:00) [72]
> Так как доп. потоки при выполнении этой КС простаивать не
> будут(так сами не вызывают эту КС)
так надо сделать, чтобы тоже они работали через крит секцию иначе труба.
DVM (2008-06-19 12:44:00) [73]
> но ведь здесь и нет никакого обращения разных потоков к
> одному коду.
Как нет? Основной поток опрашивает переменные дополнительного.
vint45 (2008-06-19 12:34:00) [74]кто-то аффтара спрашивал? здесь я.. чуствствую себя как приглашенный гость на передаче у позднера.
можно я со своей колокольни? только не ругайте сильно, я ведь только учусь
1. Кто-то просил уточнить, что делают доп. потоки.
Потоки занимаются файловыми операциями. Соответственно, вычислительная нагрузка на процессор для каждого потока достаточно мала. Каждый поток работает со своим файловым источником, и с другими доп. потоками не взаимодействует (нет никаких общих ресурсов)
2. Главная задача состоит в корректном (синхронизированном) копировании значений переменных доп. потоков к основному потоку приложения (передача статданных в основной поток для последующего отображения в форме главного потока).
Копирование значений переменных абы как, без синхронизации, или согласованности (не знаю как правильнее сказать, т.е. без проверки на то, что будут скопированы реальные значения переменных) считаю неправильным.
3. Теперь о КС. В моем понимании это секция кода, которая может выполняться только одним потоком. Я не совсем понимаю как КС можно использовать при передаче переменных без коллизий. Ведь КС по сути призваны упорядочить обращение потоков к одному куску кода, но ведь здесь и нет никакого обращения разных потоков к одному коду.
Допустим мы процедуру присвоения переменных от доп. к основному потоку с КС вызываем из основного потока. Так как доп. потоки при выполнении этой КС простаивать не будут(так сами не вызывают эту КС), то они вполне могут начать запись в свои переменные в момент присвоения и переменным главного потока может присвоиться абракатабра.
Если процедуру присвоения с КС вызывать из доп. потоков, то тоже возникают коллизии. Так как основной поток в момент вызова КС доп. потоком также простаивать не будет, и может возникнуть ситуация, когда при присвоении переменным новых значений, осн. поток одномоментно будет считывать их значения, и опять возникнут коллизии.
4. О Событиях. Опять же в моем понимании, в общем случае событие это механизм позволяющий приостановить работу всех потоков процесса, кроме потока, который инициировал ожидание этого события.
Поэтому, если мы будем инициировать ожидание события из основного потока, то получаем остановку выполнения всех доп. потоков, и делаем опять же через основной поток считывание переменных. Но так как переменные в доп. потоках в этот момент не синхронизированы, то мы можем считать абракатабру.
При вызове ожидания события из доп. потоков, все проходит вроде как шоколадно. Доп. поток вызывает ожидание, пишет синхронизированные данные в переменные основного потока, и после этого вызывает само событие, запускающие остальные потоки. Здесь смущает только то, что основной поток (да и остальные) будет часто простаивать, так как доп. потоков может быть не мало. И этот простой может оказаться критичным с точки зрения управления приложением.
Игорь Шевченко (2008-06-19 09:40:00) [75]MsGuns © (19.06.08 09:16) [63]
Серега, смотри сам - если у тебя, допустим, N потоков занимаются, ну скажем закачкой файлов и в "локальных переменных" хранят прогресс этой самой закачки, а главный поток натурально этот прогресс в красивом виде отображает. Неужели в этом случае требуется супер-пупер синхронизация с критическими или некритическими секциями ?
MsGuns (2008-06-19 09:16:00) [76]>Игорь Шевченко © (18.06.08 23:40) [62]
>Вам бы сказки писать - Шарль Перро в гробу бы перевернулся от зависти.
Да ты, Игорь, тоже не пальцем делан :)
Игорь Шевченко (2008-06-18 23:40:00) [77]Вам бы сказки писать - Шарль Перро в гробу бы перевернулся от зависти.
"Есть приложение, в котором выполняются несколько потоков. Каждый поток имеет свой локальный набор переменных. Требуется периодически считывать из главного потока значения некоторых локальных переменных этих потоков (сбор статданных, для последующего отображения статистики на экране).
Вопрос. Надо ли помещать в критическую секцию процесс чтения локальных переменных главным потоком из остальных потоков?
ps прочитал пару статей по потокам, но там в основном рассматриваются варианты, когда потоки пытаются изменять, а не читать общие переменные"
Нигде не сказано, нужны ли данные для отображения на экране "согласованные" или достаточно несогласованных (для разных потоков), а от этого в первую очередь зависит метод чтения, либо он нужен синхронизированный между всем потоками, либо он нужен синхронизированым между каждым конкретным потоком, чьи локальные переменные читаются и, соответственно, читающим потоком, либо вся синхронизация идет далеким лесом и отображающий поток просто периодически (по таймеру, например), опрашивает те самые локальные переменные потоков и кладет на согласованность, потому что соглсованность стат. данных для отображения представляется мне крайне надуманной.
MsGuns (2008-06-18 20:16:00) [78]А вот другая схема, построенная не на опросе "мамы" деток", а на оповещении "детками" "мамы" о завршении ими какого-то целостногоблока алгоритма:
В этом случае главный поток ("мама") никого не опрашивает (ессно, таймер тут побоку), а вот потоки ("дочки") по завершению блока сообщают об этом событии "маме". Вот тут как раз нужны и КС и синхронизация дабы "доклады" "деток" не "перепутывались". Но в этом случае "мама" будет весьма в затруднительном положении - каким образом ей перерисовывать на канве ту же сетку, если "доклады" будут сыпаться как из Рога изобилия - сетка просто будет безбожно моргать либо периодически подмаргивать, что будет не просто заметно визуально - это будет раздражать глаза.
Именно поэтому в ДАННОМ случае я и выбрал систему опроса, а не оповещения.
Вот если бы визуализации не было (например, если идет мониторинг запросов к скл-серверу, но отображаются только завершенные процессы), а вместо этого "мама" должна была бы сделать что-то невизуальное, но своевременное (например послать сообщение об изменении статуса запроса по сети) - вот тогда да, метод "опроса" глючен по своей сути, и вместо него следует применить метод "оповещения".
MsGuns (2008-06-18 19:59:00) [79]>Тын-Дын © (18.06.08 17:25) [59]
>Понимаешь, твой частный случй - это именно частныйслучай.
>Ты же пытаешься распространить его на общее, что является >категорически неправильным и вредным.
? Читаем мой пост, который вызвал у тебя такую реакцю:
>MsGuns © (17.06.08 12:51) [4]
> Потоки пишут в кэши через Synchronize (хотя, возможно, это и не обязательно)
А теперь винмание ! где я пытался распространить на общее?
Наоборот, я сначала упомянул о синхронизации, но затем, как частный случай, упомянул о том, что возможно, они не являются обязательными. И привел пример совершенно нормально, безо всяких чудес работающем коде.
>Мои практические советы имеют к теме топика самое непосредственное >отношение
ИМХО, мой пример более непосредственноотносится именно к топику (по крайней мере как я его понял) именно как частный случай, который не требует сложного кода с кучей try except/finally и реализуется по весьма тривиальной схеме - потоки сами по себе что-то пишут в свои собственные переменные, а гл. поток периодическиревизует эти переменные и в соответствии с ними отображает текущий статус потока (или точнее, шал алгоритма, которым занимается поток в данный момент времени) напрмер, в сетке.
Для начинающего это много проще и не требует капитального въезжания в сам процесс синхронизации, который интуитивно малопонятен человеку, не знакомому с принципами работы процессора и ОС
Этот метод при правильной организации памяти (например, через "кэши"-рекорды, "принадлежащие" активным потокам) вполне надежен и если в кэши не пихать килобайты мусора, маловероятно что будет показывать "сырую" (вот еще чудо-термин,- а что, бывает "сваренная" или "сухая" инфа ?) информацию. Визуально, по крайней мере, это незаметно.
Тын-Дын (2008-06-18 17:25:00) [80]
> Если ты хочешь рассказать про синхронизацию и, особенно,
> про КС (а ведь тебя просили !),
А я написал свои домыслы палладину. Мало разве?
> Экой ты смешной, друже ;) Устраивает не меня, а пользователей,
> и написанное вовсе не бред.
Понимаешь, твой частный случй - это именно частныйслучай.
Ты же пытаешься распространить его на общее, что является категорически неправильным и вредным.
> На бред больше похожи твои теоретические изыски под девизом
> "так делать низзя ибо будет бо-бо", к сабжу имеющие весьма
> отдаленное отношение.
Мои практическиесоветы имеют к теме топика самое непосредственное отношение (В отличие от твоих отвлечённых -"В моей программе так работает"ю А вот у ANB - не работает так. Почему бы это?).