Главная страница
    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.6 MB
Время: 0.005 c
2-1295192100
TStas
2011-01-16 18:35
2011.04.17
Как добавить в ImageList картинку с прозрачным фоном в run time?


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


8-1211477420
presston
2008-05-22 21:30
2011.04.17
Выделение области на рисунке


2-1294441804
Студент
2011-01-08 02:10
2011.04.17
Как открыть своё окно как бы "модальным" поверх чужого?


15-1294148572
alexdn_
2011-01-04 16:42
2011.04.17
c++





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский