Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2004.11.14;
Скачать: [xml.tar.bz2];

Вниз

Пул потоков.   Найти похожие ветки 

 
panov ©   (2004-10-14 17:24) [40]

Кстати, насчет TMultiReadExclusiveWriteSynchronize.

Как я понял, единственное, что мне даст, это то, что мне не надо несколько критических секций использовать, и объекты для ожидания создаются статически всего 2.


 
Суслик ©   (2004-10-14 17:27) [41]


>  [40] panov ©   (14.10.04 17:24)

я не говорил, что его надо исполозовать. Я говорил, что в его коде используется синхронизация без ОЯ. Вот и все. Причем такая синхронизация, которую не сразу и поймешь. Я вроде разобрался. Но вопротить вряд ли смогу.

Все же хотелось бы услышать ответ на вопрос [38]. Может устроить тесты и выяснить, что собственно ОЯ вполне удовлетворяют? Я бы начал с этого. Я когда-то делал такое сравнение. И скажу честно, что я не смог придумать задачу для своих целей, где бы скорострельности event"ов мне бы не хватало.


 
panov ©   (2004-10-14 17:29) [42]

>Суслик ©   (14.10.04 17:27) [41]

Смысл ведь имеет не конкретная задача, а наиболее отимальное универсальное решение.

Например, для моей задачи(а это копирование файлов в нескольких потоках) такое замедление не будет иметь последствия.

Если прикинуть, то такая задача "на скорострельность" может иметь применение при расчетах в графических динамичных приложениях...


 
Суслик ©   (2004-10-14 17:31) [43]


>  [42] panov ©   (14.10.04 17:29)

Оптимальность, понятие неоднозначное. Я уверен, что в текущей задаче путь с использование ОЯ будет наиболее оптимальный, т.к.:
1. Позволит доделать класс за 10 мин.
2. Точно не окажет негативного влияния на скорость.


 
Игорь Шевченко ©   (2004-10-14 18:07) [44]


> Все же, есть способы объходиться без ОЯ. Например см. класс
> TMultiReadExclusiveWriteSynchronizer. Там реализована очень
> скорострельная синхронизация БЕЗ объектов ядра. Если интересно
> смотри его.


constructor TMultiReadExclusiveWriteSynchronizer.Create;
begin
 inherited Create;
 InitializeCriticalSection(FLock);
 FReadExit := CreateEvent(nil, True, True, nil);  // manual reset, start signaled
 SetLength(FActiveThreads, 4);
end;

Типа того, что CreateEvent - это уже не создание объекта ядра.


 
Игорь Шевченко ©   (2004-10-14 18:08) [45]


> Создал класс для работы с пулом потоков.


Рихтер тоже создал, только на основе IOCompletion, если мне память не изменяет.


 
Суслик ©   (2004-10-14 18:11) [46]


>  [44] Игорь Шевченко ©   (14.10.04 18:07)

Игорь, хватит придираться, ты прекрасно понимаешь о чем я? Нет?

Тогда напомню, что я тебя спрашивал как работает этот класс, ты сказал, что там ничего сложного. Если так, то ты не можешь не знать, что многие действия, для которых можно было бы применить ОЯ для синхронизации обходятся без оных. В качесвте примера см. TThreadLocalCounter.

Мое утверждение было о том, что при синхронизации не обязательно использовать ОЯ. И класс был приведен в качестве пример такой реализации.

Возражения есть?


 
panov ©   (2004-10-14 18:16) [47]

>Игорь Шевченко ©   (14.10.04 18:08) [45]
Ну можно, конечно, и на основе портов ввода-вывода, только работать в w98 они не будут...


 
Игорь Шевченко ©   (2004-10-14 18:20) [48]

Суслик ©   (14.10.04 18:11) [46]


> Возражения есть?


Есть.

Посмотри, как реализованы критические секции и зачем там объект ядра LockSemaphore.


 
Суслик ©   (2004-10-14 18:30) [49]


> Посмотри, как реализованы критические секции и зачем там
> объект ядра LockSemaphore.

Где?

Есть не ошибаюсь, критические секции доходят до использования ОЯ только в случае коллизий. Источник: рихтер, win для профи, стр 202. Т.е. большинство операций выполняются в польз. режиме.
В случае же использования ОЯ переход в реж. ядра происходит сразу. Истоник: тот же. + опыт сранения быстродействия.


 
Verg ©   (2004-10-14 18:37) [50]


> Есть не ошибаюсь, критические секции доходят до использования
> ОЯ только в случае коллизий.


Не путаете ли вы понятия "обходиться без" и "использовать эффективно".
Если КС использует ОЯ эффективно, это не означает, что она их вообще не использует.


 
Суслик ©   (2004-10-14 18:38) [51]


>  [50] Verg ©   (14.10.04 18:37)

может и путаю. Но опыт сравнения быстродействия mutex и кс в свое время сказал мне о многом.


 
Игорь Шевченко ©   (2004-10-14 18:42) [52]

Суслик ©   (14.10.04 18:30) [49]

Что есть коллизия по Рихтеру ?


 
Суслик ©   (2004-10-14 18:45) [53]


> Что есть коллизия по Рихтеру ?

Сначала на вопрос ответь, где посмотреть реализацию КС?

------------
Microsoft повысила быстродействие критических секций, включив в них спин блокировку Теперь, когда Вы вызываете EnterCriticalSection, она выполняет заданное число циклов спин-блокировки, пытаясь получить доступ к ресурсу и лишь в том случае, когда все попытки закапчиваются неудачно, функция переводит поток в ре жим ядра, где он будет находиться в состоянии ожидания.


 
Суслик ©   (2004-10-14 18:47) [54]


> [52] Игорь Шевченко ©   (14.10.04 18:42)

Вместо того, чтобы ко мне придираться (:))) ответил бы Панову - на первый вопрос [23]. Все польза была бы.


 
Игорь Шевченко ©   (2004-10-14 18:48) [55]

Суслик ©   (14.10.04 18:30) [49]

Пример:

unit main;

interface
uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls;

type
 TForm1 = class(TForm)
   Button1: TButton;
   procedure Button1Click(Sender: TObject);
 end;

var
 Form1: TForm1;

implementation
uses
 Thread;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
 InitializeCriticalSection(CS);
 TMyThread.Create (false);
 TMyThread.Create (false);
end;

end.


и

unit Thread;

interface

uses
 Classes,
 Windows;

type
 TMyThread = class(TThread)
 private
 protected
   procedure Execute; override;
 end;

var
 CS: TRtlCriticalSection;

implementation

{ TMyThread }

procedure TMyThread.Execute;
begin
 EnterCriticalSection(CS);
end;

end.


Запусти и посмотри количество объектов ядра до нажатия кнопки и после. Предупреждаю, процесс лучше снимать из среды, по Ctrl+F2


 
Игорь Шевченко ©   (2004-10-14 18:49) [56]

Суслик ©   (14.10.04 18:45) [53]


> Сначала на вопрос ответь, где посмотреть реализацию КС?


windows.pas разумеется


 
Verg ©   (2004-10-14 18:51) [57]


> [51] Суслик ©   (14.10.04 18:38)
>
> >  [50] Verg ©   (14.10.04 18:37)
>
> может и путаю. Но опыт сравнения быстродействия mutex и
> кс в свое время сказал мне о многом.


Не, ну сравнивать производительнось при различных способах использования ОЯ - это уже отдельный вопрос.

Вроде шла речь об обеспечении синхронизации БЕЗ использования ОЯ вообще?

> Мое утверждение было о том, что при синхронизации не обязательно
> использовать ОЯ.


Вот и интересно как?

while lacked do sleep(1)
?

Кстати, Mutex хорош своим ObjectName, т.е. он именованый, а значит синхронизировать по "принципу КС" можно потоки из разных процессов. Вот и все, если этого не требуется, то используй КС. Конечно она эффективнее в таком случае


 
Суслик ©   (2004-10-14 18:53) [58]


> Запусти и посмотри количество объектов ядра до нажатия кнопки
> и после. Предупреждаю, процесс лучше снимать из среды, по
> Ctrl+F2

Как?
Никогда не делал.


 
ЮрийК ©   (2004-10-14 18:53) [59]

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


 
Суслик ©   (2004-10-14 18:56) [60]


> [57] Verg ©   (14.10.04 18:51)

Interlocedфункции, они же не ОЯ.
И в тоже время позволяют делать синхнонизацию. Класс TThreadLocalCounter например.

Контрольный вопрос.
В классе TThreadLocalCounter используются методы синхронизации. Например, TThreadLocalCounter.Open. Есть ли здесь ОЯ?


 
Суслик ©   (2004-10-14 19:00) [61]


> [57] Verg ©   (14.10.04 18:51)


> Вот и интересно как?


> while lacked do sleep(1)

Например. interlocked функции еще есть.


 
panov ©   (2004-10-14 19:04) [62]

>ЮрийК ©   (14.10.04 18:53) [59]

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

На самом деле, похоже, лучше эту задачу реализовать без использования TThread, что я сейчас и буду пробовать.

Конечно, вопрос с максимально работой и ожиданием без спин-блокировки все равно остается.

Но к нему придется вернуться после первых набросков.


 
panov ©   (2004-10-14 19:05) [63]

>Суслик ©   (14.10.04 19:00) [61]
Например. interlocked функции еще есть.

InterLocked-функции могут быть применены только для спин-блокировки(активного ожидания).


 
Суслик ©   (2004-10-14 19:08) [64]


> InterLocked-функции могут быть применены только для спин-блокировки(активного
> ожидания).

ну да, но это же лучше чем бегать из режима ядра и обратно, как бывает при использовании ОЯ?

ЗЫ
Скаже всем, что понятия не имею, что такое режим ядра. Знаю только, что в него надо переходить для использования ОЯ и это дорогостоящая операция (несколько 1000 тактов). Это я говорю для того, чтобы не приписвать себе знания, коих нет. Но думаю понимание того, что лучше режим ядра избегать являтеся достаточным основанием для продолжения моего участия в дискусе.
Спасибо за внимание.


 
panov ©   (2004-10-14 19:08) [65]

>ЮрийК ©   (14.10.04 18:53) [59]

При небольших изменениях в коде код в начале топика будет совершенно работоспособным, другое дело, что все-таки хочется сделать оптимальный код-)


 
panov ©   (2004-10-14 19:11) [66]

>Суслик ©   (14.10.04 19:08) [64]

ну да, но это же лучше чем бегать из режима ядра и обратно, как бывает при использовании ОЯ

Пример "почти -)" спин-блокировки привел Verg ©   (14.10.04 18:51) [57]
while lacked do sleep(1)
Вот классический пример:
while lacked do ;

Естественно, такой код загрузит на время ожидания процессор на 100%.

Такой метод применим только для гарантированно кратких по времени ожиданий.


 
Суслик ©   (2004-10-14 19:11) [67]


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

Когда сделаем, хорошо было бы его здесь увидеть :)
Т.е. я поддерживаю ЮрияК


 
Суслик ©   (2004-10-14 19:13) [68]


> Такой метод применим только для гарантированно кратких по
> времени ожиданий.

Все в наших руках - можно гарантировать чего душе угодно. Я серьезно. Нужно внимательно посмотреть на задачу - может такая гарантия есть.

Или хочестся создать суперуниверсальный супероптимальный супервсеоблемющий пул для всех случае жизни? Это миф (имхо). Кадой задаче свое решение.


 
panov ©   (2004-10-14 19:17) [69]

>Суслик ©   (14.10.04 19:13) [68]
Суперуниверсальный не нужен, конечно.

Я выше приводил пример, в котором необходима максимальная скорость различных вычислений в потоках - динамичная графика.

Кстати, даже для задачи, которой сейчас я занимаюсь(резервное копирование файлов), желательна минимальная нагрузка на процессор-)


 
Суслик ©   (2004-10-14 19:20) [70]


>  [69] panov ©   (14.10.04 19:17)

Мое мнение такое (повторяюсь) - пока не будет тестов производительности, говорить об оптимальности или отсутствии таковой рано.

У меня нет сейчас времени на опыты :( Иначе подготовил бы статистический отчет. При этом надо тестировать не просто бомбардировку ОЯ (т.е. активное их использование), а имитацию реальной нагрузки на ОЯ, (т.е. попытаться смоделировать действительность - как часто будут ОЯ использоваться в зависимости от поребностей пользователя пуллера). Уверяю, что результат удивит - разница в общем производительности будет заметна слабо (конечно, если не брать каких-то экстремальных случаев).


 
Суслик ©   (2004-10-14 19:28) [71]

И самое мое последнее мнение о направленности разработки супероптимального сервера такое: если оный нужен, то нужно очень серьезно засаживаться за соответствующую литературу.
Я видел много кода, часть на сях, часть на дельфи. Я был поражен теми изощнениями, которыми пользуются люди для оптимальности.

Вот простой пример (не относится к теме, но показывает устремленность людей, как писать оптимально)


function TStringList.Find(const S: string; var Index: Integer): Boolean;
var
 L, H, I, C: Integer;
begin
 Result := False;
 L := 0;
 H := FCount - 1;
 while L <= H do
 begin
   I := (L + H) shr 1;
   C := CompareStrings(FList^[I].FString, S);
   if C < 0 then L := I + 1 else
   begin
     H := I - 1;
     if C = 0 then
     begin
       Result := True;
       if Duplicates <> dupAccept then L := I;
     end;
   end;
 end;
 Index := L;
end;

Как вы думаете, почему не написано так (мне какжется так наглядней)

   if C < 0 then L := I + 1 else
   if C > 0 then  H := I - 1 else
   if C = 0 then
   begin
     Result := True;
     if Duplicates <> dupAccept then L := I;
   end;


Все таже пресловутая оптимальность - экономия на одном сравнении (в первом случае нет if c > 0). Понимаю, что пример оффтоповый, но мне кажется, что он показывает, что приемы оптимального программирования так просто не изобретешь - нужно планомерное чтение разной лит-ры и просмотр кода.


 
VMcL ©   (2004-10-14 21:13) [72]

>Как вы думаете, почему не написано так (мне какжется так наглядней)

 if C < 0 then L := I + 1 else
 if C > 0 then  H := I - 1 else
 if C = 0 then
 begin
  Result := True;
  if Duplicates <> dupAccept then L := I;
 end;


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

if C < 0 then L := I + 1 else
if C > 0 then  H := I - 1 else
//if C = 0 then
begin
  Result := True;
  if Duplicates <> dupAccept then L := I;
end;


 
Verg ©   (2004-10-14 22:43) [73]


> Как вы думаете, почему не написано так (мне какжется так
> наглядней)
>
>    if C < 0 then L := I + 1 else
>    if C > 0 then  H := I - 1 (* C>0 *)else
>    if C = 0 then
>    begin
>      Result := True;
>      if Duplicates <> dupAccept then L := I;
>    end;


Ой ли? Разве этот код эквивалентен


>    if C < 0 then L := I + 1 else
>    begin
>      H := I - 1; // C>=0
>      if C = 0 then
>      begin
>        Result := True;
>        if Duplicates <> dupAccept then L := I;
>      end;
>    end;


??


 
Суслик ©   (2004-10-15 11:12) [74]


> Ой ли? Разве этот код эквивалентен

нет, но делает тоже самое (по сути)


 
Игорь Шевченко ©   (2004-10-15 11:18) [75]

Суслик ©   (14.10.04 19:08) [64]

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

Разумно ?

Спин-блокировка применима для синхронизации между потоками в многопроцессорной системе, когда поток на одном процессоре входит в цикл (крутится - spin), опрашивая общую переменную.


 
Zelius ©   (2004-10-15 11:36) [76]

Кстати, посмотрел код и решил сказать, может пригодится.
Что бы не было вопрос при создании потомков TThread о том что раньше отработает, конструктор с инициализацией всех ресурсов или начнет Execute выполняться, я всегда сначала инициализирую ресурсы и только потом вызываю наследуемый конструктор:

constructor TThreadExecuter.Create(const aOwnerThread: THandle;const aNumThread: Integer);
begin
FreeOnTerminate := True;
FThreadFree := True;
FThreadIdOwner := aOwnerThread;
FNumThread := aNumThread;
inherited Create(True);
end;


 
Erik1 ©   (2004-10-15 11:56) [77]

Мастера фигей маются, как новички. Видать и им ничто человеческое нечуждо. :)
 Вобщето надо проежде всего позаботится об понятности алгоритма, его эфективновность оченивается вовсе не в количестве обращений к ядру. Вобщее правило: если алгоритм красив, то он ненуждается в улучшениях!
Суслик [71]
 В 99% случаев я бы выбрал второй вариант, проше читать. А эфективность можно уличшить как нибудь по другому.
to panov
У меня все потоки используют WaitForMultipleObjects с двумя event как минимум. Первый говорит потоку начать работу, второй терминировать поток. Это неокажет некакого замедления на расчеты, поскольку они выполняются внутри процедувы. А если расчеты очень короткие но частые, то suspend resume замедлят их еще больше. К томуже в Dlephi выще 5 версии нельзя делать resume 2 раза подряд, будет exception.


 
Суслик ©   (2004-10-15 12:18) [78]

2Игорь Шевченко

> Синхронизация потоков невозможна без диспетчеризации, согласись.
> То есть, как минимум, одни поток должен уметь сказать WaitForSingleObject
> (или какую-то разновидность), пока другой поток не освободит
> ресурс. После того, как ресурс освобожден, об этом должно
> стать известно диспетчеру, чтобы он смог запустить ожидавший
> поток.
>
> Разумно ?

Да разумно. Но WaitForSingleObject не единственное решение.

Все предложение: для того, чтобы убедится в том, что я говорю о том, о чем имею предсталение и для того, чтобы не разводить лишние разговоры, предлагаю всем изучить класс TThreadLocalCounter.

Класс TThreadLocalCounter (модуль sysutils) реализует TLS (не полноценный, а для одного конкретного целочисленного значения: TThreadInfo.RecursionCount), средствами дельфи. Причем существенно быстрее, чем TLS windows.
Идея такая: есть список (fHashTable) голов однонапрасленных списков. Хэш индекс строится исходя из GetCurrentThreadId.
Приведенный метод занимается тем, что выделяет вызвавшему потоку структуру PThreadInfo. Поиск идет либо в списке свободных структур (если поток отказывается от использования структура помечается свободной, но из списка не удаляется), либо если не нашел, то создает новую. Заметь, тут нет никаких критических секций. Внимательно читай комментарий. Собственно в тех двух строчках, которые о коментриует, и есть вся суть метода и моего утвержднеия о возможности синхронизации без ОЯ и даже без КС.

procedure TThreadLocalCounter.Open(var Thread: PThreadInfo);
var
 P: PThreadInfo;
 H: Byte;
 CurThread: Cardinal;
begin
 InterlockedIncrement(FOpenCount);
 H := HashIndex;
 P := FHashTable[H];
 CurThread := GetCurrentThreadID;

 // КАЖДОМУ ПОТОКУ ВЫДЕЛЯТЕСЯ РОВНО ОДНА СТРУКТУРА (ЭТО ЕЩЕ ОДНО ОТЛИЧИЕ ОТ TLS WINDOWS)
 while (P <> nil) and (P.ThreadID <> CurThread) do
   P := P.Next;

 // ЕСЛИ НЕ НАШЛИ, ТО...
 if P = nil then
 begin
   // ПЫТАЕМСЯ НАЙТИ В НЕИСПОЛЬЗУЕМЫХ СТРУКТРУАХ СВОБОДНУЮ
   P := Recycle;

   // ЕСЛИ ТАКОВЫХ НЕТ, ТО СОЗДАЕМ НОВУЮ
   if P = nil then
     P := PThreadInfo(AllocMem(sizeof(TThreadInfo)));

   P.ThreadID := CurThread;

   // А ВОТ ТЕПЕРЬ ЕЕ НУЖНО ВСТАВИТЬ В СПИСОК. ЧИТАЙ КОМЕНТАРИЙ.

   // Another thread could start traversing the list between when we set the
   // head to P and when we assign to P.Next.  Initializing P.Next to point
   // to itself will make others loop until we assign the tail to P.Next.
   P.Next := P; ????? ВОТ ЭТО МЕСТО МЕНЯ ПОТРЯСЛО!
   P.Next := PThreadInfo(InterlockedExchange(Integer(FHashTable[H]), Integer(P)));
 end;
 Thread := P;
end;


После этого продолжим обсуждение синхронизацию доступа к общему ресурсу без ОЯ и КС.


 
Игорь Шевченко ©   (2004-10-15 12:29) [79]

Суслик ©   (15.10.04 12:18) [78]

В этом коде модификация списка, которая может затронуть другие потоки, выполняется одной функцией InterlockedExchange. Строчка, которая тебя поразила - это искусственное действие, направленное на то, чтобы модификацию можно было выполнить той самой единственной функцией.
InterlockedExchange реализуется единственной командой (не считая подготовки аргументов) lock cmpxchg, которая в данном случае успешно может использоваться для модификации, так как является атомарной (не прерываемой в середине исполнения) и атомарной же на многопроцессорной системе, так как префикс lock выдает сигнал на блокировку шины доступа к памяти.

Точно такие же приемы используются при работе с длинными строками.

Но синхронизацией это можно назвать с огромной натяжкой, так как осноная цель в этом коде - это реализовать потокобезопасную модификацию списка.


 
Суслик ©   (2004-10-15 12:33) [80]


>  [79] Игорь Шевченко ©   (15.10.04 12:29)

Определение синхронизации в студию!



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

Форум: "Основная";
Текущий архив: 2004.11.14;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.66 MB
Время: 0.041 c
14-1098646074
Dimedrol
2004-10-24 23:27
2004.11.14
Блоки питания - 250W -vs- 400W


6-1094221548
Wahnsinng
2004-09-03 18:25
2004.11.14
создание сайта


1-1098909761
Мирон
2004-10-28 00:42
2004.11.14
Нужно проверить возможность создания каталога, не создавая его


14-1098562409
Soft
2004-10-24 00:13
2004.11.14
nousa.in.ua


3-1097824876
nv
2004-10-15 11:21
2004.11.14
MIDAS - информация о клиенте.





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский