Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2011.04.17;
Скачать: CL | DM;

Вниз

Организация многопоточности   Найти похожие ветки 

 
Leonid Troyanovsky ©   (2009-08-17 13:29) [40]


> Kolan ©   (17.08.09 13:15) [39]

> Что такое TRTLCriticalSection? В справке нет.

From Turbo Delphi for Win32 help:

TRTLCriticalSection stores information about a Windows critical section.

From Windows.pas (find declaration):
TRTLCriticalSection = _RTL_CRITICAL_SECTION; // &etc.

--
Regards, LVT.


 
Kolan ©   (2009-08-17 16:15) [41]

Благодарю.

А чем принципиально исользование TRTLCriticalSection отличается от использования TCriticalSection?

Кстати в 2009 Делфи для этого типа есть два хелпера:

 TCriticalSectionHelper = record helper for TRTLCriticalSection
   procedure Initialize; inline;
   procedure Destroy; inline;
   procedure Free; inline;
   procedure Enter; inline;
   procedure Leave; inline;
   function TryEnter: Boolean; inline;
 end;

 TConditionVariableHelper = record helper for TRTLConditionVariable
 public
   class function Create: TRTLConditionVariable; static;
   procedure Free; inline;
   function SleepCS(var CriticalSection: TRTLCriticalSection; dwMilliseconds: DWORD): Boolean;
   procedure Wake;
   procedure WakeAll;
 end;


 
Дмитрий Белькевич   (2009-08-17 16:26) [42]


> record helper for


Тоже увидел. Вопрос, кстати, образовался. Чем эти хэлперы отличаются от обычного наследования?


 
Kolan ©   (2009-08-17 16:29) [43]

Ну наследования структур нет. А кроме того, как наследованием добавить методы в середину бибилиотечной иерархии? — Только исправив библиотеку. А бибилиотека может быть и без исходников, кроме того VCL какой-нибудь править — не самое безопасное занятие.

А тут раз: helper for TComponent написал и все.


 
Дмитрий Белькевич   (2009-08-17 18:03) [44]

Ясно, буду разбираться.


 
Leonid Troyanovsky ©   (2009-08-17 21:12) [45]


> Kolan ©   (17.08.09 16:15) [41]

> А чем принципиально исользование TRTLCriticalSection отличается
> от использования TCriticalSection?

Принципиально, наверное, не отличается.
Как-то обвык без объектов и Enter|Leave :(

--
Regards, LVT.


 
Vitaliy_____   (2009-08-18 08:29) [46]


> Leonid Troyanovsky ©

Идею твоей программы понял, там кстати целые числа - вполне подошел бы InterlockedExchange, твоя идея для вещественных лучше. Идея плохая :) Где гарантия, что порожденный поток не уснет надолго? (скажем, на час :) ) - тогда "главный" просчитает часть цикла, отпишется через критическую секцию и начнет считать дальше, ведь так? Просчитает ЕЩЕ ОДНУ часть цикла и снова увеличит ct.calcid := ct.calcid + 1;
При мелких вычислениях - очень реальная ситуация. Что при этом будет - а также как у меня где-то проскок через контрольную точку. Хотя, если еще одну контрольную ID-шку добавить, подход сработает. Только по сути это будет спин-блокировка, что крайне не рекомендуется тем же Рихтером.
Поправьте, если не прав...


 
Leonid Troyanovsky ©   (2009-08-18 10:12) [47]


> Vitaliy_____   (18.08.09 08:29) [46]

> Идею твоей программы понял, там кстати целые числа - вполне
> подошел бы InterlockedExchange, твоя идея для вещественных

Да хоть для комплексных ;)

> Где гарантия, что порожденный поток не уснет надолго? (скажем,
>  на час :) ) -

Эту гарантию должен дать разработчик.

> тогда "главный" просчитает часть цикла, отпишется через
> критическую секцию и начнет считать дальше, ведь так?

Если вторичный поток будет, например, вытеснен не осводив КС,
то первичный, досчитав свой цикл, будет ожидать ее освобождения.
Для гуева потока это может и не очень, но я на гуи и не настаивал.

> Только по сути это будет спин-блокировка, что крайне не
> рекомендуется тем же Рихтером.

Чего-то я такой рекомендации не помню.
Спин-блокировка - самый быстрый механизм синхронизации.
При использовании же КС потоки, все же, попадают в режим ядра.

--
Regards, LVT.


 
Vitaliy_____   (2009-08-18 12:12) [48]


> Если вторичный поток будет, например, вытеснен не осводив
> КС,
> то первичный, досчитав свой цикл, будет ожидать ее освобождения.
>
> Для гуева потока это может и не очень, но я на гуи и не
> настаивал.

А если все же освободит и уснет в момент проверки while not Application.Terminated do?
Согласен, редкий случай, но возможный. В моем примере тоже по 10-20 итераций делает (то есть по 30 000 образов, в них по 4 барьера) и это * число итераций, а потом вдруг умирает. К счастью, стабильно :)
Нет, должно работать 100%.
А по поводу спин-блокировки, у рихтера это "Худшее что можно сделать"... стр. 195 (215 в моей электронной версии). Сам ее "придумал", потом прочитал, проникся и больше так не делаю :)
Пусть попадают в режим ядра, у меня при большой задаче не такие частые синхронизации, и можно пренебречь 1000 тактов ради того чтобы прога не мешала другим. А вот о "надежности" барьеров - придется самому разбираться, как время найду - хочу добить задачку, может потом статью накатаю :-[c]


 
Leonid Troyanovsky ©   (2009-08-18 13:35) [49]


> Vitaliy_____   (18.08.09 12:12) [48]

> А если все же освободит и уснет в момент проверки while
> not Application.Terminated do?

Если вторичный поток вытеснен, то первичный узнает об этом
сравнив ID & calcid, ну и вторую часть ему придется считать самому.


> А по поводу спин-блокировки, у рихтера это "Худшее что можно
> сделать"...

Контекст непонятен. Ладно, посмотрим на досуге.

> Пусть попадают в режим ядра, у меня при большой задаче не
> такие частые синхронизации

Два Event с автосбросом, первичный поток сигналит одним для старта очередной итерации и ждет сигнал готовности от вторичного.

Ну и, конечно, внутри процесса имена объектам не нужны.

--
Regards, LVT.


 
Vitaliy_____   (2009-08-18 13:51) [50]

А если у нас появляется новый проц с 4 ядрами, то 4 event? Нет, не разубедил относительно семафоров.


 
Leonid Troyanovsky ©   (2009-08-18 14:45) [51]


> Vitaliy_____   (18.08.09 13:51) [50]

> А если у нас появляется новый проц с 4 ядрами, то 4 event?

Я ж ссылочкой снабжал, насчет эффективной многопоточности.

--
Regards, LVT.


 
Vitaliy_____   (2009-08-19 06:09) [52]

А я даже все статьи прочитал... Это было интересно - но если я не ошибаюсь, для корных процов проблем нет. У меня Core 2 duo e6750 @ 2.66GHz. Да и личный замер, как я писал в [33] более чем убеждает в этом. Хотя у меня в руках теперь хороший тестер, могу сам проверить на других :) Только ждать долго - хорошее разделение нагрузки только на реальной (читать большой) задаче - а ее потом для сравнения надо еще и на однопоточной версии проги прогнать.


 
Vitaliy_____   (2009-08-26 12:59) [53]

Переписал модуль, да обрадуется Leonid Troyanovsky, 2 события для синхронизации достаточно (ну еще счетчик с InterlockedIncrement) :) Пока работает, кому интересно можно потестировать.

Повторюсь, суть этой штуки - барьер с автосбросом - то есть несколько потоков могут "легко и непринужденно" собираться в контрольной точке (вызывая WaitBarrier), при этом когда они дождутся следующий барьер будет автоматически поставлен. Изначально необходимо только проинициализировать число потоков, на которые выставляется барьер.
Если кто протестирует - отпишитесь.


unit BarrierSinchronization;

interface

uses Windows, Utils, SysUtils, SyncObjs;

type TMyBarrierSinchronization=class(TObject)
private
 NP:integer;                            // Число потоков для синхронизации
 MBSEvent1,MBSEvent2:THandle;   // 2 события для реализации барьера
 EvName1,EvName2:String;                // Имена событий
 state:boolean;                         // Ключ, с каким событием работаем
 Counter: integer;                      // Внутренний счетчик выполненности заданий
public
 procedure Initialize(NumProcesses:integer);
 procedure WaitBarrier(const ThreadState:string); overload; // Отладочная версия
 procedure WaitBarrier(); overload;

 constructor Create;
 destructor destroy; override;
end;

var MyBarrierSinchronization:TMyBarrierSinchronization;

resourcestring
WinAPIErrMsg2="Can not create event 1 for synchronization!";
WinAPIErrMsg3="Can not create event 2 for synchronization!";
WinAPIErrMsg4="WaitTimeOut on Event 1!";
WinAPIErrMsg5="WaitTimeOut on Event 2!";
implementation

constructor TMyBarrierSinchronization.Create;
begin
EvName1:=MyUtils.GetUnicalName+"MBSMyEvent";
EvName2:=EvName1+"2";
EvName1:=EvName1+"1";
// Создаем события
MBSEvent1:=CreateEvent(nil,true,false,PChar(EvName1)); // Событие с ручным сбросом, изначально занято
if GetLastError<>0 then raise Exception.Create(WinAPIErrMsg2);
MBSEvent2:=CreateEvent(nil,true,false,PChar(EvName2)); // Событие с ручным сбросом, изначально занято
if GetLastError<>0 then raise Exception.Create(WinAPIErrMsg3);
end;

destructor TMyBarrierSinchronization.destroy;
begin
if MBSEvent1<>0 then
 CloseHandle(MBSEvent1);
if MBSEvent2<>0 then
 CloseHandle(MBSEvent2);
inherited;
end;

procedure TMyBarrierSinchronization.Initialize(NumProcesses:integer);
begin
NP:=NumProcesses;
Counter:=0;
ResetEvent(MBSEvent1);
ResetEvent(MBSEvent2);
State:=true;
end;

procedure TMyBarrierSinchronization.WaitBarrier(const ThreadState:string);
var key:cardinal;
begin
if state then begin // На нечетных барьерах ждем первое событие, выставляем второе
 InterlockedIncrement(Counter);
 if Counter=NP then begin
  ResetEvent(MBSEvent2);
  Counter:=0;
  State:=false;
  SetEvent(MBSEvent1); // ПОСЛЕДНИМ шагом запустили все потоки
 end else begin
  repeat
  key:=WaitForSingleObject(MBSEvent1,3000);
   if key=WAIT_TIMEOUT then begin
    MessageBox(0,PChar(WinAPIErrMsg4+#13+ThreadState),PChar(IntToStr(GetCurrentThreadID)),0);
    DebugBreak;
   end;
  until key=WAIT_OBJECT_0;
 end;
end else begin      // На четных барьерах наоборот
 InterlockedIncrement(Counter);
 if Counter=NP then begin
  ResetEvent(MBSEvent1);
  Counter:=0;
  State:=true;
  SetEvent(MBSEvent2);
 end else begin
  repeat
   key:=WaitForSingleObject(MBSEvent2,3000);
   if key=WAIT_TIMEOUT then begin
    MessageBox(0,PChar(WinAPIErrMsg5+#13+ThreadState),PChar(IntToStr(GetCurrentThreadID)),0);
    DebugBreak;
   end;
  until key=WAIT_OBJECT_0;
 end;
end;
end;

procedure TMyBarrierSinchronization.WaitBarrier();
var key:cardinal;
begin
if state then begin // На нечетных барьерах ждем первое событие, выставляем второе
 InterlockedIncrement(Counter);
 if Counter=NP then begin
  ResetEvent(MBSEvent2);
  Counter:=0;
  State:=false;
  SetEvent(MBSEvent1); // ПОСЛЕДНИМ шагом запустили все потоки
 end else begin
  key:=WaitForSingleObject(MBSEvent1,INFINITE);
 end;
end else begin      // На четных барьерах наоборот
 InterlockedIncrement(Counter);
 if Counter=NP then begin
  ResetEvent(MBSEvent1);
  Counter:=0;
  State:=true;
  SetEvent(MBSEvent2);
 end else begin
   key:=WaitForSingleObject(MBSEvent2,INFINITE);
 end;
end;
end;

initialization
MyBarrierSinchronization:=TMyBarrierSinchronization.Create;

end.


// Utils - модуль с утилитами, из него дергается только GetUnicalName - на основе времени создает уникальную текстовую прибавку к имени - можно убрать, т.к. в рамках 1 процесса можно и неименованные события использовать.


 
Leonid Troyanovsky ©   (2009-08-26 14:37) [54]


> Vitaliy_____   (26.08.09 12:59) [53]

> if GetLastError<>0 then raise Exception.Create(WinAPIErrMsg2);

Win32Check(MBSEvent1<>0);

MessageBox я б заменил на OutputDebugString, смотреть можно
в дельфийском EventLog.

У WaitForSingleObject есть еще WAIT_FAILED.

> т.к. в рамках 1 процесса можно и неименованные события

А для 2 процессов и InterlockedIncrement не годится.

--
Regards, LVT.


 
Vitaliy_____   (2009-08-27 09:05) [55]


> У WaitForSingleObject есть еще WAIT_FAILED.


Да. Обработки ошибки ожидания тут нет... Можно добавить.


> А для 2 процессов и InterlockedIncrement не годится.


Да точно, мне в принципе для разных не надо. Хотя чуток напильником поработать и можно будет для разных использовать (взять тот же оптекс у Рихтера за основу).


> MessageBox я б заменил на OutputDebugString


Вот тут не согласен - MessageBox удобен тем, что блокирует поток на время - я вижу на экране несколько сообщений от разных потоков, в нужном давлю ок и через DebugBreak попадаю в отладку именного этого потока (то есть хотя сообщения возникнуть могут не все сразу, я могу подождать и отлаживать именно интересующий меня поток).


 
Суслик_   (2009-08-28 14:17) [56]

Если физический проц один, то только потеряешь производительность, а не получишь ее.


 
Vitaliy_____   (2009-09-09 13:23) [57]

Leonid Troyanovsky, [26]
> Могу заметить, чтобы написать и отладить не очень сложный
> составной объект синхронизации нужна масса времени и терпения.

Кажется придется согласиться :)
Код в [53] все еще не рабочий :) Как я понимаю проблему, у нас "защищен" Counter, но недостаточно защищена переменная state. Редко, но все-таки возникает ситуация "прорывания барьера". Долго не мог понять почему, т.к. возникает крайне редко - тяжело найти. Как раз та самая паршивая ситуация, когда 1 поток "засыпает на долго". Решил потестировать что будет при 4 потоках на двух ядрах, конкуренция возросла, стало появляться чаще. Ох уж ента штука... :)
Попробовал извернуться так:

...
MBSCrSect:TCriticalSection;
...

procedure TMyBarrierSinchronization.WaitBarrier(const ThreadState:string);
var key:cardinal;
   WaitintEvent,ResetingEvent:THandle;
begin
MBSCrSect.Enter;
 if state then begin  // На нечетных барьерах ждем первое событие, выставляем второе
  WaitintEvent:=MBSEvent1;
  ResetingEvent:=MBSEvent2;
 end else begin       // На четных барьерах наоборот
  WaitintEvent:=MBSEvent2;
  ResetingEvent:=MBSEvent1;
 end;

 inc(Counter);
 if Counter=NP then begin
  if (ResetEvent(ResetingEvent)=false) then
   raise Exception.Create(WinAPIErrMsg6);
  Counter:=0;
  State:=(state=false);
  SetEvent(WaitintEvent); // запустили все остальные потоки
 end;
MBSCrSect.Leave; // Все критичные данные в локальных переменных, гонка не возможна.

repeat
 key:=WaitForSingleObject(WaitintEvent,7000);
 if key=WAIT_TIMEOUT then begin
  MessageBox(0,PChar(WinAPIErrMsg4+#13+ThreadState),PChar(IntToStr(GetCurrentThreadID)),0);
  DebugBreak;
 end;
until key=WAIT_OBJECT_0;
end;



 
Vitaliy_____   (2009-09-10 05:41) [58]

Я кажется наконец понял, что там могло быть:
InterlockedIncrement(Counter);
if Counter=NP then begin
 ResetEvent(MBSEvent2);
 Counter:=0;
 State:=false;
 SetEvent(MBSEvent1); // ПОСЛЕДНИМ шагом запустили все потоки
end else begin
 repeat
 key:=WaitForSingleObject(MBSEvent1,3000);
  if key=WAIT_TIMEOUT then begin
   MessageBox(0,PChar(WinAPIErrMsg4+#13+ThreadState),PChar(IntToStr(GetCurrentThreadID)),0);
   DebugBreak;
  end;
 until key=WAIT_OBJECT_0;
end;

Похоже, ситуация такая: InterlockedIncrement(Counter); нам гарантирует, что Counter будет "честно" увеличен. А вот дальнейшее сравнение if Counter=NP небезопасно. Похоже получалось так: поток увеличивал счетчик и засыпал. После этого последний поток увеличивал счетчик и делал "свои дела", но до Counter:=0 не успевал еще дойти. Просыпался "уснувший" и, опа, он тоже считал себя последним, т.к. Counter=NP все еще истина. Вот и получаем ДВА последних потока. Уже ошибка, хотя событиям все равно сколько через них пройдет (кстати, когда делал на семафорах - там не все равно, потому и вылетало стабильно). Однако может быть так (хотя не спорю, достаточно редко):
У нас два "последних" потока. Один из них запускает все остальные, а второй спит. И если вдруг какой-то поток проснувшись дойдет до след. барьера (т.е. увеличит счетчик) и лишь после этого проснется "лишний последний поток" и выполнит counter:=0 - вот тогда и возникает ошибка. Похоже так. Естественно, вероятность ситуации повышается с увеличением числа потоков.
----------
Чувствую глубокое удовлетворения от познания дебрей ВинАПИ :)



Страницы: 1 2 вся ветка

Текущий архив: 2011.04.17;
Скачать: CL | DM;

Наверх




Память: 0.61 MB
Время: 0.01 c
3-1256535483
JohnLemon
2009-10-26 08:38
2011.04.17
Помогите с SQL запросом....


2-1291397443
Олег Крапивин
2010-12-03 20:30
2011.04.17
COM-технология и Дельфи 2009


15-1293625833
dmk
2010-12-29 15:30
2011.04.17
Можно ли сделать общий загрузчик


2-1295279025
SamBrown
2011-01-17 18:43
2011.04.17
Как убрать колонки у VCL ListView (vsReport)


15-1293565837
TUser
2010-12-28 22:50
2011.04.17
Почему эбонит электризуется при натерании об шерсть?