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

Вниз

Странный deadlock   Найти похожие ветки 

 
mt2   (2008-01-14 15:37) [0]

Hi,

unit Unit1;
// Delphi-7
// Странный deadlock, см. строки:
// Почему эта программа зависает без sleep(0) ?
// Если n=200, то  эта программа работает без sleep(0). Почему?

// Испытано под MS Windows XP SP2, CPU Intel Pentium-4 3.0 GHz
// и на другом CPU Intel Pentium-4 3.2 GHz

interface

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

type

 TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   procedure Button1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

 TTestThread = class(TThread)
   constructor Create (CreateSuspended : Boolean; eventHnd : THandle; i : integer);
 private
   { Private declarations }
   ind : integer;
   syncEvent : THandle;
 protected
   procedure Execute; override;
 end;

var
 Form1: TForm1;
 events  : array [0..7] of THandle;
 thr : array [0..7] of TTestThread;
 stop : Boolean;
 n : integer;
 calc : array [1..8] of integer;

implementation

{$R *.dfm}

constructor TTestThread.Create (CreateSuspended : Boolean; eventHnd : THandle; i : integer);
begin
inherited Create (CreateSuspended);
syncEvent := eventHnd;
ind := i;
end;

procedure TTestThread.Execute;
begin
 { Place thread code here }
 repeat
   inc(calc [ind],n);
   SetEvent (syncEvent);
   suspend;
 until stop;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 i : integer;

begin
for i:=1 to 8 do
 calc [i] := i;

stop := false;

for i:=0 to 7 do
 events [i] := CreateEvent (nil, true, false, PChar("testEvent"+intToStr(i)));

for i:=0 to 7 do
 thr[i] := TTestThread.Create(true, events [i],i+1);

n := 0;
repeat
 inc (n);

 thr[0].Resume;
 thr[1].Resume;
 thr[2].Resume;
 thr[3].Resume;
 thr[4].Resume;
 thr[5].Resume;
 thr[6].Resume;
 thr[7].Resume;

 waitForMultipleObjects (8, @events, true, INFINITE);
 ResetEvent (events [0]);
 ResetEvent (events [1]);
 ResetEvent (events [2]);
 ResetEvent (events [3]);
 ResetEvent (events [4]);
 ResetEvent (events [5]);
 ResetEvent (events [6]);
 ResetEvent (events [7]);

 sleep(0); // Почему эта программа зависает без sleep(0) ?

until n=1000; // Если n=200, то  эта программа работает без sleep(0). Почему?

Memo1.Lines.Add("n= "+ intToStr(n));
for i:=1 to 8 do
 Memo1.Lines.Add(intToStr(calc[i]));
end;

end.


 
Сергей М. ©   (2008-01-14 15:44) [1]

Поздравляем тебя с имеющими тебя странностями.


 
mt2   (2008-01-14 15:48) [2]

:) может они (странности) не только мои? но и ОС? - тогда и тебя поздравляю ;))))))))))


 
Сергей М. ©   (2008-01-14 16:13) [3]

Что это вообще за хня ?

Изложи ТЗ.


 
mt2   (2008-01-14 16:36) [4]

ТЗ  будет слишком долго, прога сложная, отловил bug и минимизировал. Лишние детали будут не по теме. Попробуй для начала запустить у себя и сообщи, что получается, и почему.


 
icWasya ©   (2008-01-14 17:09) [5]

после waitForMultipleObjects (8, @events, true, INFINITE);
ResetEvent не нужно


 
Сергей М. ©   (2008-01-14 17:11) [6]


> отловил bug и минимизировал


И ?


> Лишние детали будут не по теме


Продолжай париться.


> Попробуй для начала запустить


Да нахрен оно мне надо


> что получается, и почему.


Получится хня.

Почему ?

потому что абракадабра в коде.


 
ага   (2008-01-14 19:40) [7]


> Если n=200, то  эта программа работает без sleep(0)

Эт тока кажется. А виснет вот почему.

Такой момент, когда все потоки одного успели вызвать SetEvent и уйти в Suspend.  В какой-то момент последний поток успевает вызвать SetEvent и  в этот момент планировщик у него отбирает процессор и отдает гланому - главный-то поток интерфейсный, у его окна фокус, потому у него приоритет повыше.

Главный поток сбрасывает все эвенты и резюмит все потоки. Все кроме последнего - он-то еще не успел уснуть и Resume ему по барабану. Далее главный поток уходит в WaitFor... и процессор достаетс последнему из потоков. Тот просыпается и вызывает свой Suspend.

Все, аллес. Спит главный поток, потому что у нег WaitAll = true, спит наш несчастный последний поток в своем Suspend. Отработают свои циклы и уснут все остальные потоки. Все спят, и разбудить их некому. Кроме TerminateProcess.

Б-р-р-р...


 
ага   (2008-01-14 19:45) [8]

А, ну да, еще ж про Sleep. Sleep усыпляет на квант планировщика главный поток, в результате наш несчастный "последний" поток получает возможность вызвать свой Suspend раньше, чем главный поток вызовет ему Resume.


> Сергей М. ©   (14.01.08 17:11) [6]

Ну че абракадабра-то? Нормально сделал, выдернул проблему, лишнего не постил.


 
ага   (2008-01-14 19:46) [9]


> Такой момент, когда все потоки одного успели вызвать

Такой момент, когда все потоки кроме одного успели вызвать


 
ага   (2008-01-14 19:49) [10]

На многопроцессорной тачке это могло долго не проявиться. А могло и сразу. В общем, все единл фтопку.


 
mt2   (2008-01-14 21:41) [11]


> ага   (14.01.08 19:40) [7]
>
> > Если n=200, то  эта программа работает без sleep(0)
>
> Эт тока кажется. А виснет вот почему.
>
> Такой момент, когда все потоки одного успели вызвать SetEvent
> и уйти в Suspend.  В какой-то момент последний поток успевает
> вызвать SetEvent и  в этот момент планировщик у него отбирает
> процессор и отдает гланому - главный-то поток интерфейсный,
>  у его окна фокус, потому у него приоритет повыше.
>
> Главный поток сбрасывает все эвенты и резюмит все потоки.
>  Все кроме последнего - он-то еще не успел уснуть и Resume
> ему по барабану. Далее главный поток уходит в WaitFor...
>  и процессор достаетс последнему из потоков. Тот просыпается
> и вызывает свой Suspend.
>
> Все, аллес. Спит главный поток, потому что у нег WaitAll
> = true, спит наш несчастный последний поток в своем Suspend.
>  Отработают свои циклы и уснут все остальные потоки. Все
> спят, и разбудить их некому. Кроме TerminateProcess.
>
> Б-р-р-р...


Огромное спасибо за объяснение! - Ребус Вами разгадан. А можете посоветовать, как грамотно исправить этот модельный пример, чтобы всегда работал? Пожалуйста, не сочтите за труд - было бы очень интересно увидеть Ваш вариант кода.


 
ага   (2008-01-14 22:17) [12]

А я бы Suspend не использовал. Дал бы потокам еще по эвенту и пусть они их ждут вместо Suspend. А в главном вместо Resume взводил бы эти эвенты. Или там сообщения использовал бы, или порт завершения, да мало ли. Это так, на вскидку, особо не задумываясь - задачи-то я не знаю, а без этого искать решение - дело бесперспективное.


 
mt2   (2008-01-14 23:02) [13]


> ага   (14.01.08 22:17) [12]
> А я бы Suspend не использовал. Дал бы потокам еще по эвенту
> и пусть они их ждут вместо Suspend. А в главном вместо Resume
> взводил бы эти эвенты. Или там сообщения использовал бы,
>  или порт завершения, да мало ли. Это так, на вскидку, особо
> не задумываясь - задачи-то я не знаю, а без этого искать
> решение - дело бесперспективное.


Ну, насчет знания задачи - свои задачи каждый решает сам ;) А здесь задача представлена модельным кодом и только им: как надежнее и эффективнее исправить данный код? То что он никакой особо полезной работы не делает, абсолютно ничего не значит. Главное - методика, когда она найдена, можно и полезной работой догрузить. Существенное видно из кода: каждый поток работает очень небольшое время, а потоков 8. Понятно, что эффективнее, когда большее время - но это другой вопрос, о котором много сказано, и он очевиден.


 
Leonid Troyanovsky ©   (2008-01-14 23:26) [14]


> mt2   (14.01.08 23:02) [13]

> ;) А здесь задача представлена модельным кодом и только
> им: как надежнее и эффективнее исправить данный код? То
> что он никакой особо полезной работы не делает, абсолютно
> ничего не значит.

Смело выкидывай его в корзину, бо, он ничего не значит и ничего
особо полезного не делает, и плотно приступайся к разработке ТЗ.

--
Regards, LVT.


 
ага   (2008-01-15 05:42) [15]


> mt2   (14.01.08 23:02) [13]

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

>каждый поток работает очень небольшое время

Вот это уже чуть конкретнее. Тогда пожалуй Completion Port.

>а потоков 8

А вот это уже не постановка задачи, а черт знает что. Кто сказал, что их 8? Почему 8? Почему не 7 или 9? Ваш папа любит цифру 8?
Хошь обижайся, хошь нет, но лично у меня такая конкретика вызывает подозрение в наличии системной ошибки. Очень редко, если вообще когда-либо, возникают задачи, требующие точно заданного числа потоков.


 
ketmar ©   (2008-01-15 07:44) [16]

>[15] ага (15.01.08 05:42)
>Очень редко, если вообще когда-либо, возникают задачи, требующие точно
>заданного числа потоков.

отчего же? очень часто такие задачи возникают. где точно задано число потоков: 1.


 
mt2   (2008-01-15 12:38) [17]


> ага   (15.01.08 05:42) [15]
>
> > mt2   (14.01.08 23:02) [13]
>
> Тут вишь ли дело какое - при программировании многопоточности
> именно тонкости реализации выходят на передний план, так
> как набор средств для решения вполне себе детерминирован
> и не особо поддается расширению. И эффективность решения
> именно этими деталями, их комбинациями и определяется. А
> это уже прямо и даже непосредственно определяется конкретикой
> стоящей задачи. Вот и выходит, что попытки создания решения
> для абстрактных задач в этой области абрактные же решения
> и дают, которые тока для умственной гимнастики и годятся,
>  а практического толку с них ноль.

Всевозможные библиотеки для многопоточности - ни что иное, как обобщенные решения ;)

>
> >каждый поток работает очень небольшое время
>
> Вот это уже чуть конкретнее. Тогда пожалуй Completion Port.

Ok. А можно подробнее для данного случая? Обычно Completion Port применяется для I/O...

>
>
> >а потоков 8
>
> А вот это уже не постановка задачи, а черт знает что. Кто
> сказал, что их 8? Почему 8? Почему не 7 или 9? Ваш папа
> любит цифру 8?
> Хошь обижайся, хошь нет, но лично у меня такая конкретика
> вызывает подозрение в наличии системной ошибки. Очень редко,
>  если вообще когда-либо, возникают задачи, требующие точно
> заданного числа потоков.
> <Цитата>



В задачах графики/псевдо-графики у каждого пиксела 8 соседей.


 
ага   (2008-01-15 15:09) [18]


> ketmar ©   (15.01.08 07:44) [16]


> отчего же? очень часто такие задачи возникают. где точно
> задано число потоков: 1.

Эт точно:)))

> В задачах графики/псевдо-графики у каждого пиксела 8 соседей
+
Ну теперь я точно уверен, что 8 потоков тут нафиг не нужны:))-
Соседей могет быть сколь угодно. Но количество процессоров на машине от этого не зависит. Почитай вот здесь
http://delphikingdom.com/asp/answer.asp?IDAnswer=58287
пост от [09-01-2008 11:21]. В той ветке и пример имеется. А можно и для твоей модели

unit Unit1;

interface

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

type
 TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   Label1: TLabel;
   procedure Button1Click(Sender: TObject);
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
 private
   { Private declarations }
   FPort: TCompletionPort;
 public
   { Public declarations }
 end;

 TTestThread = class(TThread)
   constructor Create (CreateSuspended : Boolean; CPort: TCompletionPort);
 private
   { Private declarations }
   FPort: TCompletionPort;
 protected
   procedure Execute; override;
 end;

var
 Form1: TForm1;
 events  : array [0..7] of THandle;
 thr : array [0..7] of TTestThread;
 stop : Boolean;
 n : integer;
 calc : array [1..8] of integer;

implementation

{$R *.dfm}

constructor TTestThread.Create (CreateSuspended : Boolean; CPort: TCompletionPort);
begin
 inherited Create (CreateSuspended);
 FPort:= CPort;
end;

procedure TTestThread.Execute;
var
 Ind, Key: Cardinal;
 pOvp: POverlappedEx;
begin
{ Place thread code here }
 while FPort.WaitCompletion(Ind, Key, pOvp) do
 begin
   if Key = 0 then Break;
   inc(calc [Ind + 1],n);
   SetEvent (Events[Ind]);
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
i : integer;

begin
 for i:=1 to 8 do  calc [i] := i;

 n := 0;
 repeat
   inc (n);
   for i:= 0 to 7 do FPort.SetCompletion(i, 1, TOverlappedEx(nil^));
   waitForMultipleObjects (8, @events, true, INFINITE);
   ResetEvent (events [0]);
   ResetEvent (events [1]);
   ResetEvent (events [2]);
   ResetEvent (events [3]);
   ResetEvent (events [4]);
   ResetEvent (events [5]);
   ResetEvent (events [6]);
   ResetEvent (events [7]); { }
//    Label1.Caption:= IntToStr(n);
//    Label1.Refresh;

 until n=1000; // &#197;&#241;&#235;&#232; n=200, &#242;&#238;  &#253;&#242;&#224; &#239;&#240;&#238;&#227;&#240;&#224;&#236;&#236;&#224; &#240;&#224;&#225;&#238;&#242;&#224;&#229;&#242; &#225;&#229;&#231; sleep(0). &#207;&#238;&#247;&#229;&#236;&#243;?

 Memo1.Lines.Add("n= "+ intToStr(n));
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 i: integer;
begin
 FPort:= TCompletionPort.Create(8); // Но лучше ноль передать
 for i:=0 to 7 do
   thr[i]:= TTestThread.Create(false, FPort);
 for i:=0 to 7 do
   events [i] := CreateEvent (nil, true, false, nil);
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
 i: integer;
begin
 for i:= 0 to 7 do FPort.SetCompletion(0, 0, TOverlappedEx(nil^));
 for i:= 0 to 7 do thr[i].Free;
 FPort.Free;
end;

end.

Код CompletionPort вот здесь есть
http://delphikingdom.com/asp/answer.asp?IDAnswer=56098


 
mt2   (2008-01-16 00:47) [19]


> > В задачах графики/псевдо-графики у каждого пиксела 8 соседей
> +
> Ну теперь я точно уверен, что 8 потоков тут нафиг не нужны:
> ))-
> Соседей могет быть сколь угодно. Но количество процессоров
> на машине от этого не зависит.


Один мой знакомый очень не любит спрашивать незнакомых людей. Оказавшись в незнакомом ему городе, он может часами искать нужную улицу и дом, но никогда не спросит прохожего «Как пройти…», а оказавшись без часов (и без мобильника), он лучше опоздает, но не спросит «Который час?» А все потому, что давным-давно, впервые оказавшись в инете, он имел неосторожность спросить о том, как выбирать коврик для мыши. Его расспросили о конфигурации «писи», и он выслушал множество комментариев о том, какой он козел, что купил такое, его расспросили о софте, который он использует и тоже откомментировали, его расспросили о круге чтения и о знакомых, о родственниках и о родословной, выспросили, страдал ли кто из них хроническим алкоголизмом и не было ли припадочных и т.д. Про коврик ему ничего не ответили… Иногда я искренне удивляюсь, насколько человек, куда-то спешивший и остановленный вопросом «Который час?», вдруг готов бросить все дела и заниматься только твоими проблемами. Ему бесконечно мало сказать «7:45», нет, он хочет знать о тебе все, он искренне хочет помочь, он интересуется, зачем тебе знать время, не спешишь ли ты на встречу или на свидание, если да, то с кем, если девушка, то нужна ли она тебе и не совершаешь ли ты роковую ошибку, а вдруг девушка тебе  нафиг не нужна, но ты, не зная это, обманешься и увлечешься красивой внешностью и в результате вся твоя жизнь будет разбита, как в мексиканском мыльнике и т.д. Ранее Вы писали мне:


> Хошь обижайся, хошь нет


Так вот, обижаться я на тебя не буду (и не надейся ;), т.к. сильно мне помог с анализом задачи. Но и ты на меня не обижайся, пожалуйста! Ну, попробуй представить, что могут быть особые условия. Что софт, например, делается для конкретной конфигурации. Или, например, представь себе препода, который хочет показать своим студентам как не надо. Он придет к тебе за советом о реализации рекурсивного вычисления факториала, а ты начнешь его убеждать, что во многих учебниках написано, что рекурсивная реализация не лучший метод для вычисления факториала… В конце концов, может я хочу написать в отчете, что был испробован ряд очевидных возможностей распараллеливания, в том числе и на 8 потоков по соседям и, как и следовало ожидать, этот подход себя не оправдал;)

За решение спасибо, но так не получается; причины:

1) отсутствует файл {$I CompVersionDef.inc}

2) EAccessViolation в методе TCompletionPort.SetCompletion

Видимо, тут нужен контроль и на старте и на финише, чтобы "спринтеры" не разбредались и возвращались на старт организованно ;) Т.е. каждый спринтер получат от основного потока 2 команды: "старт!" - это значит нужно бежать дистанцию от старта до финиша и "на старт" - вернуться с финиша на старт. WaitForSingleObject (startEvent,INFINITE) в TTestThread.Execute гарантирует от фальш-старта, WaitForSingleObject (finishEvent,INFINITE) разрешает возвращение от финиша к старту для очередного забега. Основной поток, подав команду  "старт!", должен дождаться, когда все финишируют, подать команду "на старт" и дождаться, когда все будут готовы к старту.

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

unit Unit1;
// Delphi-7, MS Windows XP SP2, CPU Intel Pentium-4 3.0 GHz

interface

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

type
 TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   procedure Button1Click(Sender: TObject);
   procedure FormCreate(Sender: TObject);
 private
 public
 end;

 TTestThread = class(TThread)
   constructor Create (CreateSuspended : Boolean; eventHnd : THandle; i : integer);
 private
   ind : integer;
   syncEvent : THandle;
 protected
   procedure Execute; override;
 end;

var
 Form1: TForm1;
 events  : array [0..7] of THandle;
 startEvent, finishEvent  : THandle;
 thr : array [0..7] of TTestThread;
 stop : Boolean;
 n : integer;
 calc : array [1..8] of integer;

implementation

{$R *.dfm}

constructor TTestThread.Create (CreateSuspended : Boolean; eventHnd : THandle; i : integer);
begin
inherited Create (CreateSuspended);
syncEvent := eventHnd;
ind := i;
FreeOnTerminate := true;
end;

procedure TTestThread.Execute;
begin
 repeat
   WaitForSingleObject (startEvent,INFINITE); // -> старт?

   inc(calc [ind]); // "забег"

// Можно так:
//    SetEvent (syncEvent);  // финиш ->
//    WaitForSingleObject (finishEvent,INFINITE); // -> вернуться на старт?

// А можно и так:
   SignalObjectAndWait(syncEvent,finishEvent,INFINITE,False);

   SetEvent (syncEvent); // к старту готов ->
 until stop;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 i : integer;

begin
for i:=1 to 8 do
 calc [i] := 0;

stop := false;
n := 0;
repeat
 inc (n);
 
 SetEvent (startEvent); // старт! ->
 waitForMultipleObjects (8, @events, true, INFINITE); // -> все финишировали?
 ResetEvent (startEvent);
 for i:=0 to 7 do
   ResetEvent (events [i]);

 SetEvent (finishEvent); // вернуться на старт ->
 waitForMultipleObjects (8, @events, true, INFINITE);// -> к старту готов?
 ResetEvent (finishEvent);
 for i:=0 to 7 do
   ResetEvent (events [i]);

until n=1000;

Memo1.Lines.Add("n= "+ intToStr(n));
for i:=1 to 8 do
 Memo1.Lines.Add(intToStr(calc[i]));
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 i : integer;
begin
for i:=0 to 7 do
 events [i] := CreateEvent (nil, true, false, PChar("testEvent"+intToStr(i)));

startEvent := CreateEvent (nil, true, false, "startEvent");
finishEvent  := CreateEvent (nil, true, false, "finishEvent");

for i:=0 to 7 do
 thr[i] := TTestThread.Create(true, events [i],i+1);

for i:=0 to 7 do
  thr[i].Resume;
end;

end.


 
ага   (2008-01-16 06:17) [20]


>Но и ты на меня не обижайся, пожалуйста!

Да вроде не на что:)

>Ну, попробуй представить,

Попробовал - не получается. Это я так шучу, если че:))

>1) отсутствует файл {$I CompVersionDef.inc}

Да там чисто дефайны версий компилера, свободно выбрасывается

>2) EAccessViolation в методе TCompletionPort.SetCompletion

Нет там никаких AV, я ж код запускал, прежде чем постить. И как раз на XP SP2, какое совпадение:) Ищи че сам накосячил. Ну на крайняк передавай реальную структуру вместо TOverlappedEx(nil^), хотя и без того всегда работало.

>Хотелось бы услышать мнения об этом решении

А это реализация вот этого:

>ага   (14.01.08 22:17) [12]
А я бы Suspend не использовал. Дал бы потокам еще по эвенту и пусть они их ждут вместо Suspend. А в главном вместо Resume взводил бы эти эвенты.

Нормальное решение, если не учитывать, что будет работать медленнее, чем в варианте, когда число потоков = числу процессоров. Это я из вредности:)) Ну не верю я, что оно когда-нить будет работать на 8-процессорной тачке, хоть ты меня убей:)) А даже если их 8, процессоров-то - разумно запустить 7 потоков, а 8-й элемент обработать главным потоком, а потому уже ждать остальные. Элементарно, если вынести обработку в отдельную функцию.

>Иногда я искренне удивляюсь,

А че удивляться-то? Отвечает не автоматы, обычные люди. И говорят они то, что считают нужным они сами, а не спрашивающий. Хотя бы потому, что зачастую видят проблему куда ширше, чем задавший вопрос. Имеют право, разве нет?


 
ага   (2008-01-16 06:27) [21]


>А это реализация вот этого

Ан нет, не совсем. Медленнее будет, чем только со стартерам, но отдельным для каждого потока


 
ага   (2008-01-16 07:02) [22]

А можно и с одним стартером

unit Unit1;
// Delphi-7, MS Windows XP SP2, CPU Intel Pentium-4 3.0 GHz

interface

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

type
TForm1 = class(TForm)
  Button1: TButton;
  Memo1: TMemo;
  procedure Button1Click(Sender: TObject);
  procedure FormCreate(Sender: TObject);
private
public
end;

TTestThread = class(TThread)
  constructor Create (CreateSuspended : Boolean; eventHnd : THandle; i : integer);
private
  ind : integer;
  syncEvent : THandle;
protected
  procedure Execute; override;
end;

var
Form1: TForm1;
events  : array [0..7] of THandle;
startEvent, finishEvent  : THandle;
thr : array [0..7] of TTestThread;
stop : Boolean;
n : integer;
calc : array [1..8] of integer;
ThreadCounter: integer;

implementation

{$R *.dfm}

constructor TTestThread.Create (CreateSuspended : Boolean; eventHnd : THandle; i : integer);
begin
inherited Create (CreateSuspended);
syncEvent := eventHnd;
ind := i;
FreeOnTerminate := true;
end;

procedure TTestThread.Execute;
begin
repeat
  WaitForSingleObject (startEvent,INFINITE); // -> старт?
  if 0 = InterLockedDecrement(ThreadCounter) then ResetEvent(startEvent);

  inc(calc [ind]); // "забег"

  SetEvent (syncEvent); // к старту готов ->
until stop;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
i : integer;

begin
for i:=1 to 8 do
calc [i] := 0;

stop := false;
n := 0;
repeat
inc (n);
ThreadCounter:= 8;
SetEvent (startEvent); // старт! ->
waitForMultipleObjects (8, @events, true, INFINITE); // -> все финишировали?
for i:=0 to 7 do
  ResetEvent (events [i]);

until n=1000;

Memo1.Lines.Add("n= "+ intToStr(n));
for i:=1 to 8 do
Memo1.Lines.Add(intToStr(calc[i]));
end;

procedure TForm1.FormCreate(Sender: TObject);
var
i : integer;
begin
for i:=0 to 7 do
events [i] := CreateEvent (nil, true, false, PChar("testEvent"+intToStr(i)));

startEvent := CreateEvent (nil, true, false, "startEvent");
finishEvent  := CreateEvent (nil, true, false, "finishEvent");

for i:=0 to 7 do
thr[i] := TTestThread.Create(true, events [i],i+1);

for i:=0 to 7 do
 thr[i].Resume;
end;

end.

Это я правда не компилировал - нечем - но работать должно. А можно и PulseEvent использовать.


 
ага   (2008-01-16 07:10) [23]

:))) Да и syncEvent можно один на всех

var
 CompleteCounter: integer;

procedure TTestThread.Execute;
begin
 repeat
   WaitForSingleObject (startEvent,INFINITE); // -> старт?
   if 0 = InterLockedDecrement(ThreadCounter) then ResetEvent(startEvent);

   inc(calc [ind]); // "забег"

 if 0 = InterLockedDecrement(CompleteCounter) then SetEvent (syncEvent); // к старту готов ->
 until stop;
end

procedure TForm1.Button1Click(Sender: TObject);
var
i : integer;

begin
for i:=1 to 8 do
calc [i] := 0;

stop := false;
n := 0;
repeat
inc (n);
ThreadCounter:= 8;
CompleteCounter:= 8;
SetEvent (startEvent); // старт! ->
waitForSingleObject (CompleteEvent, INFINITE); // -> все финишировали?
ResetEvent (CompleteEvent);

until n=1000;
...

Здесь CompleteEvent - один эвент с ручным сбросом вместо массива events[0..7]


 
ага   (2008-01-16 08:35) [24]

Поспешишь - людей насмешишь:( Фигню я написал в последних постах. Сейчас вернулся, посмотрел спокойно - полная фигня. Не будет остановки потоков в WaitForSingleObject (startEvent,INFINITE), эвент-то не сброшен. В общем, не обращай на это внимание.

Быстрее всего будет при использовании CompletionPort или, если без порта, при отдельном стартере на каждый поток.


 
mt2   (2008-01-16 22:57) [25]

>Отвечает не автоматы, обычные люди. И говорят они то, что считают нужным они сами, а не спрашивающий. Хотя бы потому, что зачастую видят проблему куда ширше, чем задавший вопрос. Имеют право, разве нет?<

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

>Нормальное решение, если не учитывать, что будет работать медленнее, чем в варианте, когда число потоков = числу процессоров. Это я из вредности:)) Ну не верю я, что оно когда-нить будет работать на 8-процессорной тачке, хоть ты меня убей:)) А даже если их 8, процессоров-то - разумно запустить 7 потоков, а 8-й элемент обработать главным потоком, а потому уже ждать остальные. Элементарно, если вынести обработку в отдельную функцию. <

Решая одну задачку, я решил попробовать в том числе и экстравагантное решение, и натолкнулся на казус, о котором мой первый пост. Мне показалось интересным этот казус обсудить. При этом, прежде всего, я обсуждал конкретный казус и узко ограничивал рамки обсуждения. А то мы бы так и проговорили про общеизвестные вещи, типа «число потоков = числу процессоров». Теперь, когда цель достигнута и казус разъяснился, можем поговорить и шире. Кстати, этот код в решение я включать, видимо, не буду (по тем же самым соображениям, которые ты высказал), хотя Интелу (для которого это делается) под силу взять любую из существующих в природе тачек –даже из тех, что нет в магазинах;) Но в любом случае, в отчете напишу, что в числе возможных опробовал и такое решение – за это дадут дополнительные баллы:)

А теперь о конкретной задачке и о проблеме. Задачка эта конкурсная, подробнее см. – http://softwarecommunity-rus.intel.com/articles/rus/61032.htm - игра Жизнь, в альтернативном алгоритме, который задан в виде кода на С (исх.код можно выгрузить там же), его нужно распараллелить. Это 4-й тур, а всего их 12, примкнуть можно в любой момент. Вот и посмотри, может, тебя заинтересует, а то проблема… - Теперь о проблеме: проблема в том, что я там пока один на Дельфи пишу, хотя и успешно, в предыдущем туре с 11 места передвинулся на 8, при том, что за быстродействие мои решения получили максимальные баллы, но языки в неравном положении по отношению к С++ (или С-- :) подробнее можешь почитать пост на форуме конкурса: http://softwarecommunity-rus.intel.com/isn/Community/ru-RU/forums/thread/30220903.aspx И было бы лучше, если бы еще кто-то сильный на Дельфи был в конкурсе…

В отношении CompletionPort – жаль, я сразу, когда ты упомянул, не понял, что это оригинальный модуль, а то бы сказал, что не подойдет из-за заморочек с копирайтом. Но  все равно модуль меня заинтересовал, и я его посмотрю подробнее, только позже, когда сдам это задание, а то сейчас уже время поджимает – я ведь не только для конкурса пишу, еще и работа есть;)) Пока первые запуски прошли, как я написал. Т.е. {$I CompVersionDef.inc} я, конечно, удалил, хотя такие вещи меня всегда настораживают, а EAccessViolation стабильно повторяется. Надо будет разбираться, почему.


 
ага   (2008-01-17 07:27) [26]


>ты говоришь правильные и хорошо известные вещи

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

>Право имеют, а вот что видят проблему шире, не факт.

Конечно не факт, ты говоришь правильные и хорошо известные вещи:)) Бывают исключения, но в большинстве случаев это все-же так. У меня большой форумный опыт, из него и сделал обобщение. К сожалению форумы, изначельно задуманные как профессиональные клубы для взимопомощи и консультаций c коллегами, постепенно превратились в ликбез, где одна группа практически только задает вопросы, а другая, относительно узкая, - только отвечает:((

А по поводу конкурсов - да не прикалывают они меня, всяческие конкурсы, жаль на них время тратить. К тому же там все на JavaScript, а он у меня отключен из прынципу. А уж споры на тему, какой язык толще...э-э, то бишь круче - это ваще без меня.

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

Да какой там нафиг копирайт:))) Три вызова апишных функций, завернутых в элементарный класс:)) Ну используй эти функции напрямую, без класса, никакого копирайта:)


 
mt2   (2008-01-17 22:20) [27]


> ага   (17.01.08 07:27) [26]
>
> >ты говоришь правильные и хорошо известные вещи
>
> Если правильные и хорошо известные, так почему тогда "я
> искренне удивляюсь"? Да ладно, фиг с ним.


Да я не тому удивляюсь, а тому, когда спрашиваешь про одно, а в ответ слышишь  правильное, но хорошо известное… Да ладно, фиг с ним:)))


>
> >Право имеют, а вот что видят проблему шире, не факт.
>
> Конечно не факт, ты говоришь правильные и хорошо известные
> вещи:)) Бывают исключения, но в большинстве случаев это
> все-же так. У меня большой форумный опыт, из него и сделал
> обобщение. К сожалению форумы, изначельно задуманные как
> профессиональные клубы для взимопомощи и консультаций c
> коллегами, постепенно превратились в ликбез, где одна группа
> практически только задает вопросы, а другая, относительно
>  узкая, - только отвечает:((


У меня опыт Инета с 1987 г ;)
На счет ликбеза – 1) сами виноваты – чуть посложнее задача и что, кроме тебя и меня много еще конструктивных мнений в этом топике? Кому-то, видимо, нравится отвечать только на очевидные вопросы… 2) может, мне показалось, но и у тебя иногда проскальзывают менторские нотки (у других больше) – мне по-фигу, но не всем коллегам такое будет приятно, вот никто никого (из группы «только отвечает») ни о чем и спрашивать не хочет, чтобы за чайника не приняли – а ведь ежику ясно, что если один нечайник разобрался в апишной хххХхххХхх и потратил на это дня два, а другой не тратил и спросил, он бы может всего день потратил сам, но стоит ли каждому изобретать велосипед? Но почему-то к спросившему сразу относятся как к чайнику. 3) В конкурсе общение на более высоком уровне. Никто пока не пытается никого учить тому, что такое «мать» и родина и как их любить…Уникальная возможность общаться без названных форумных заморочек. Но, впрочем, твое дело, раз не прикалывают.


> А по поводу конкурсов - да не прикалывают они меня, всяческие
> конкурсы, жаль на них время тратить. К тому же там все на
> JavaScript, а он у меня отключен из прынципу. А уж споры
> на тему, какой язык толще...э-э, то бишь круче - это ваще
> без меня.


На мой взгляд, JavaScript лучше Java тем, что от первого меньше вреда, но я тоже когда могу отключаю. От них много зла и гадостей.

«Кто толще» - хорошая стартовая точка для выхода на нетривиальные вопросы, а то начинаешь с чего-то конкретного и получается в лучшем случае ликбез. Конечно, как всегда, все зависит от публики, чаще всего ничего не выходит.


> >не понял, что это оригинальный модуль, а то бы сказал,
> что не подойдет из-за заморочек с копирайтом
>
> Да какой там нафиг копирайт:))) Три вызова апишных функций,
>  завернутых в элементарный класс:)) Ну используй эти функции
> напрямую, без класса, никакого копирайта:)


ОК. Позже разберусь. А все-таки скажи, чем он так хорош? У тебя есть опыт использования? Какие-нибудь сравнения?

И еще, возвращаясь к решению, но не отменяя сказанного: еще одна идея – решение просто редуцируется под число процессоров, т.е. можно из двух потоков сделать один:

procedure TtestThread41.Execute;
// similar TtestThread42.Execute, …,TtestThread21.Execute,..

begin
repeat
  WaitForSingleObject (startEvent,INFINITE); // -> старт?

  inc(calc [ind]); // "забег"
  inc(calc [ind+1]); // ++++++++++++++


  SignalObjectAndWait(syncEvent,finishEvent,INFINITE,False);

  SetEvent (syncEvent); // к старту готов ->
until stop;
end;


а можно из четырех один. Запрашиваем перед запуском потоков, сколько процессоров (можно у ОС, можно у пользователя – м.б. тот не хочет все занимать) и запускаем соответствующие потоки, и так достигается «число потоков = числу процессоров», имеют смысл 3 варианта на 2,4,8 процессоров.


 
Leonid Troyanovsky ©   (2008-01-17 23:07) [28]


> mt2   (17.01.08 22:20) [27]

> У меня опыт Инета с 1987 г ;)
> На счет ликбеза – 1) сами виноваты – чуть посложнее задача
> и что, кроме тебя и меня много еще конструктивных мнений
> в этом топике? Кому-то, видимо, нравится отвечать только
> на очевидные вопросы… 2) может, мне показалось, но и у тебя
> иногда проскальзывают менторские нотки (у других больше)
> – мне по-фигу, но не всем коллегам такое будет приятно,
> вот никто никого (из группы «только отвечает») ни о чем
> и спрашивать не хочет, чтобы за чайника не приняли

А ты воздержись от самохваления и перейди к суровой правде жизни.  
Например, потрудись формулировать вопросы надлежащим образом.

Хотя,  если уж за 20 лет общения не постигнута даже такая простая
вещь как сложность общения честных людей с анонимами, то, IMHO,
трудно рассчитывать на успех и в этом деле.

Ну, а рассуждения о редукции под число процессоров на фоне
ненайденного источника AV выглядят, мягко говоря, нелепо.

Да и книжечку б неплохо б было почитать хоть к.л.

--
Regards, LVT.


 
ага   (2008-01-18 18:54) [29]


> А все-таки скажи, чем он так хорош?

Он имеет полную информацию о состоянии процессоров и на ее основе строит макимально оптимальную стратегию управления пулом потоков, выбирая оптимальное в данный текущий момент количество активных потоков и по возможности избегая лишних переключений контекстов. Особенно эфективен, когда потоки выполняют какие-нито операции, связанные с ожиданием - дисковые там, или коммуникативные. Сделать все это вручную было бы черзвычайно трудно, если вообще возможно в user mode.
Наконец он просто удобен. Почитать о нем достаточно подробно можно у Рихтера  в книге по W2k.

> У тебя есть опыт использования?

Опыт есть, но не в таких масштабах, чтобы можно было привести впечатляющие цифры. Однако выигрыш достаточно заметен, если конечно остальным кодом его не сожрать.


 
mt2   (2008-01-18 19:08) [30]


> ага   (18.01.08 18:54) [29]
>
> > А все-таки скажи, чем он так хорош?
>
> Он имеет полную информацию о состоянии процессоров и на
> ее основе строит макимально оптимальную стратегию управления
> пулом потоков, выбирая оптимальное в данный текущий момент
> количество активных потоков и по возможности избегая лишних
> переключений контекстов. Особенно эфективен, когда потоки
> выполняют какие-нито операции, связанные с ожиданием - дисковые
> там, или коммуникативные. Сделать все это вручную было бы
> черзвычайно трудно, если вообще возможно в user mode.
> Наконец он просто удобен. Почитать о нем достаточно подробно
> можно у Рихтера  в книге по W2k.
>
> > У тебя есть опыт использования?
>
> Опыт есть, но не в таких масштабах, чтобы можно было привести
> впечатляющие цифры. Однако выигрыш достаточно заметен, если
> конечно остальным кодом его не сожрать.


Ok. Спасибо!



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

Форум: "WinAPI";
Текущий архив: 2008.11.23;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.62 MB
Время: 0.007 c
2-1223557692
AlexDan
2008-10-09 17:08
2008.11.23
RichEdit и буфер обмена


4-1200314257
mt2
2008-01-14 15:37
2008.11.23
Странный deadlock


4-1200314598
toboom
2008-01-14 15:43
2008.11.23
Расшаривание папок в Win XP


2-1223623025
stas
2008-10-10 11:17
2008.11.23
dbf и кодировка


2-1223575339
Первокласник Вася
2008-10-09 22:02
2008.11.23
Свойство SQL Query1





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