![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Как избежать гонок в потоках [D6, WinXP]
leonidus © (2005-05-31 10:38:00)Мастера подскажите пожалуйста. Я написал небольшую программу, она скачивает новости с разных сайтов, и затем парсит каждую скачанную страницу на предмет нужной информации. Т.е. программа состоит из бвух блоков - блок закачки (качается сразу в несколько потоков, для каждой страницы с новостями свой поток) и блок парсинга (в нем также предусмотрена многопоточность, при этом для каждой скачанной страницы должен запускаться свой экземпляр одного и тогоже потока). Результаты парсинга (заголовки новостей) помещаются в массив. И вот в чем проблема. Если одновременно скачаются две страницы и одновременно начнут работать два парсера, то может так получиться, что они будут обращаться к одному элементу массив, что бы записать в него полученный заголовок новости. Т.е. я тап понимаю это ситуация гонок. Подскажите как этого избежать. Может есть какие-то статьи по этому поводу, я что-то ничего толком не нашел. Заранее спасибо.
leonidus (2005-06-01 10:55:00) [61]Ок, тобишь процедура работы с массивом должна выглядеть так? :
procedure TThread_work.Execute;
begin
FCS.Enter;
try
inc(nom);
a[nom]:=st1;
inc(nom);
a[nom]:=st2;
inc(nom);
a[nom]:=st3;
synchronize(showresult);
finally
FCS.Leave;
end;
end;
т.е. synchronizeиспользую до FCS.Leave так?
[13] "Нифига подобного. Никто не мешает другому потоку менять содержимое массива во время исполнения метода Syncronize в данном коде."
но к моменту вызова synchronize поток уже свое дело с массивом сделал, т.е. в принципе уже мождо давать доступ к массиву и другому потоку... но с другой стороны критическая секйия еще не закрылась, почему это другой поток должен в нее войти?
КаПиБаРа (2005-06-01 10:48:00) [62]Рекомендую почитать
http://mbo88.narod.ru/ToC.html
GrayFace (2005-06-01 10:48:00) [63]evvcom © (01.06.05 8:30) [22]</>
См. [13].
Defunct © (01.06.05 0:24) [19]
Ненадежно, может подвиснуть всё, из-за любого случайного исключения.
Да, че-то я совсем про try забыл.
FCS.Enter;
try
inc(nom);
a[nom]:=t.st;
synchronize(showresult);
finally
FCS.Leave;
end;
Еще вариант: всю работу с массивом и Memo поместить в одну процедуру и делать ее Synchronize.
Игорь Шевченко (2005-06-01 10:43:00) [64]leonidus © (01.06.05 10:42) [23]
Чукча не читатель ? Чукча write-only ?
> procedure TThread_work.Execute;
> begin
> FCS.Enter;
> inc(nom);
> a[nom]:=t.st1;
> inc(nom);
> a[nom]:=t.st2;
> inc(nom);
> a[nom]:=t.st3;
Убрать нахрен t.
leonidus (2005-06-01 10:42:00) [65]Да защищать нужно будет только запись в массив, правда операция может быть продолжительной т.к. массив 1.000.000 элементов, а что продолжительность работы критической секции столь важно? На другой поток конечно переключаться не буду, пока данный полностью не отработает.
А почему поменять местами? Я сейчас сделал так, и прекрасно работает:
TThread_work = class(TThread)
private
{ Private declarations }
st1:string;
st2:string;
st3:string;
protected
procedure Execute; override;
procedure showresult;
public
end;
var
Form1: TForm1;
FCS:TCriticalSection;
t:TThread_work;
a:array[1..100] of string;
nom,n:integer;
implementation
{$R *.dfm}
procedure TThread_work.showresult;
var
i:integer;
begin
inc(n);
if n=2 then
begin
for i:=1 to nom do
begin
form1.memo1.Lines.Add(a[i]);
end;
form1.memo1.Lines.Add('---');
end
end;
procedure TThread_work.Execute;
begin
FCS.Enter;
inc(nom);
a[nom]:=t.st1;
inc(nom);
a[nom]:=t.st2;
inc(nom);
a[nom]:=t.st3;
synchronize(showresult);
FCS.Leave;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FCS:=TCriticalSection.Create;
n:=0;
nom:=0;
t:=TThread_work.Create(true);
t.FreeOnTerminate:=true;
t.st1:='поток1: message1';
t.st2:='поток1: message2';
t.st3:='поток1: message3';
t.Resume;
t:=TThread_work.Create(true);
t.FreeOnTerminate:=true;
t.st1:='поток2: message1';
t.st2:='поток2: message2';
t.st3:='поток2: message3';
t.Resume;
end;
evvcom (2005-06-01 08:30:00) [66]
> leonidus © (01.06.05 07:57) [21]
Что у тебя FCS защищает? Массив? Вот и защищай только массив. Используй блокировки как можно на меньшее время. И тем более не переключайся на другой поток, оставив заблокированной критическую секцию. В концовке надо поменять местами строки:
FCS.Leave; // Сначала разблокируй
synchronize(showresult); // а потом в другой поток
leonidus (2005-06-01 07:57:00) [67]>Defunct что значит "может подвиснуть всё, из-за любого случайного исключения" поясните плиз мысль?
Marser (2005-06-01 00:37:00) [68]
> Вообще, CriticalSection работает гораздо быстрее, чем
> Synchronize. Но для общения с контролами от
> Synchroniz'а, обычно, никуда не убежать.
Вот это оно. TCriticalSection - инкапсуляция очень полезного объекта API. потому и быстрая. И для невизуальных данным прекрасно подходит.
Synchronize реализует тот же принцип исключения многопоточного доступа к данным, но эти данные - объекты VCL, а основная цель - исключение одновременного доступа к DC.
P.S. А ещё есть
InterlockedCompareExchange
InterlockedDecrement
InterlockedExchange
InterlockedExchangeAdd
InterlockedIncrement
Defunct (2005-06-01 00:24:00) [69]FCS.Enter;
inc(nom);
a[nom]:=t.st;
synchronize(showresult);
FCS.Leave;
end;
Ненадежно, может подвиснуть всё, из-за любого случайного исключения.
GrayFace (2005-05-31 23:41:00) [70]leonidus © (31.05.05 21:29) [17]
ок, значит мой пример никик к нормальному виду переделать не удасться?
Да он почти правильный. Ну вот так:
FCS.Enter;
inc(nom);
a[nom]:=t.st;
synchronize(showresult);
FCS.Leave;
end;
Только не вздумай в осн.потоке вызвать WaitForMultipleObjects и т.п. для потоков-обработчиков!
Вообще, CriticalSection работает гораздо быстрее, чем Synchronize. Но для общения с контролами от Synchroniz'а, обычно, никуда не убежать.
PS: Все это ИМХО.
leonidus (2005-05-31 21:29:00) [71]ок, значит мой пример никик к нормальному виду переделать не удасться?
Erik1 (2005-05-31 17:17:00) [72]Пример тебе хороший нужен. У меня есть неплохой с использованием TMultiReadExclusiveWriteSynchronizer. Там два списка в потоках обрабатываются, но 11кб фаил занимает, там разумеется дополнительная логика присудствует.
Игорь Шевченко (2005-05-31 16:39:00) [73]Alexander Panov © (31.05.05 16:23) [14]
В примере два потока, кроме основного.
Alexander Panov (2005-05-31 16:23:00) [74]Игорь Шевченко © (31.05.05 16:04) [13]
Никто не мешает другому потоку менять содержимое массива
Так в примере подразумевается, что доугих потоков нет(кроме основного, конечно).
Игорь Шевченко (2005-05-31 16:04:00) [75]Alexander Panov © (31.05.05 15:41) [11]
> Для того и Synchronize
Нифига подобного. Никто не мешает другому потоку менять содержимое массива во время исполнения метода Syncronize в данном коде.
RTFM.
Семен Сорокин (2005-05-31 15:45:00) [76]
> Alexander Panov © (31.05.05 15:41) [11]
> Семен Сорокин © (31.05.05 15:37) [10]
> а то что мы в мемо одновременно двумя потоками пишем?
>
> Для того и Synchronize;)
все понял :)
>leonidus © (31.05.05 15:31) [8]
Признаю, не прав, см.[9].
Alexander Panov (2005-05-31 15:41:00) [77]Семен Сорокин © (31.05.05 15:37) [10]
а то что мы в мемо одновременно двумя потоками пишем?
Для того и Synchronize;)
Семен Сорокин (2005-05-31 15:37:00) [78]
> а при чем тут synchronize(showresult)?
> как будет привильно?
то что глобальные переменные меняются через КС - это нормально, а то что мы в мемо одновременно двумя потоками пишем?
Игорь Шевченко (2005-05-31 15:37:00) [79]
> a[nom]:=t.st;
a[nom] := st;
leonidus (2005-05-31 15:31:00) [80]а при чем тут synchronize(showresult)?
как будет привильно?