Конференция "WinAPI" » Странный deadlock [D7, WinXP]
 
  • mt2 (14.01.08 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.

  • Сергей М. © (14.01.08 15:44) [1]
    Поздравляем тебя с имеющими тебя странностями.
  • mt2 (14.01.08 15:48) [2]
    :) может они (странности) не только мои? но и ОС? - тогда и тебя поздравляю ;))))))))))
  • Сергей М. © (14.01.08 16:13) [3]
    Что это вообще за хня ?

    Изложи ТЗ.
  • mt2 (14.01.08 16:36) [4]
    ТЗ  будет слишком долго, прога сложная, отловил bug и минимизировал. Лишние детали будут не по теме. Попробуй для начала запустить у себя и сообщи, что получается, и почему.
  • icWasya © (14.01.08 17:09) [5]
    после waitForMultipleObjects (8, @events, true, INFINITE);
    ResetEvent не нужно
  • Сергей М. © (14.01.08 17:11) [6]

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


    И ?


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


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


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


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


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


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

    Почему ?

    потому что абракадабра в коде.
  • ага (14.01.08 19:40) [7]

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

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

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

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

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

    Б-р-р-р...
  • ага (14.01.08 19:45) [8]
    А, ну да, еще ж про Sleep. Sleep усыпляет на квант планировщика главный поток, в результате наш несчастный "последний" поток получает возможность вызвать свой Suspend раньше, чем главный поток вызовет ему Resume.


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

    Ну че абракадабра-то? Нормально сделал, выдернул проблему, лишнего не постил.
  • ага (14.01.08 19:46) [9]

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

    Такой момент, когда все потоки кроме одного успели вызвать
  • ага (14.01.08 19:49) [10]
    На многопроцессорной тачке это могло долго не проявиться. А могло и сразу. В общем, все единл фтопку.
  • mt2 (14.01.08 21:41) [11]

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


    Огромное спасибо за объяснение! - Ребус Вами разгадан. А можете посоветовать, как грамотно исправить этот модельный пример, чтобы всегда работал? Пожалуйста, не сочтите за труд - было бы очень интересно увидеть Ваш вариант кода.
  • ага (14.01.08 22:17) [12]
    А я бы Suspend не использовал. Дал бы потокам еще по эвенту и пусть они их ждут вместо Suspend. А в главном вместо Resume взводил бы эти эвенты. Или там сообщения использовал бы, или порт завершения, да мало ли. Это так, на вскидку, особо не задумываясь - задачи-то я не знаю, а без этого искать решение - дело бесперспективное.
  • mt2 (14.01.08 23:02) [13]

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


    Ну, насчет знания задачи - свои задачи каждый решает сам ;) А здесь задача представлена модельным кодом и только им: как надежнее и эффективнее исправить данный код? То что он никакой особо полезной работы не делает, абсолютно ничего не значит. Главное - методика, когда она найдена, можно и полезной работой догрузить. Существенное видно из кода: каждый поток работает очень небольшое время, а потоков 8. Понятно, что эффективнее, когда большее время - но это другой вопрос, о котором много сказано, и он очевиден.
  • Leonid Troyanovsky © (14.01.08 23:26) [14]

    > mt2   (14.01.08 23:02) [13]

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

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

    --
    Regards, LVT.
  • ага (15.01.08 05:42) [15]

    > mt2   (14.01.08 23:02) [13]

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

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

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

    >а потоков 8

    А вот это уже не постановка задачи, а черт знает что. Кто сказал, что их 8? Почему 8? Почему не 7 или 9? Ваш папа любит цифру 8?
    Хошь обижайся, хошь нет, но лично у меня такая конкретика вызывает подозрение в наличии системной ошибки. Очень редко, если вообще когда-либо, возникают задачи, требующие точно заданного числа потоков.
  • ketmar © (15.01.08 07:44) [16]
    >[15] ага (15.01.08 05:42)
    >Очень редко, если вообще когда-либо, возникают задачи, требующие точно
    >заданного числа потоков.

    отчего же? очень часто такие задачи возникают. где точно задано число потоков: 1.
  • mt2 (15.01.08 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 соседей.
  • ага (15.01.08 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 (16.01.08 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.

  • ага (16.01.08 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-й элемент обработать главным потоком, а потому уже ждать остальные. Элементарно, если вынести обработку в отдельную функцию.

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

    А че удивляться-то? Отвечает не автоматы, обычные люди. И говорят они то, что считают нужным они сами, а не спрашивающий. Хотя бы потому, что зачастую видят проблему куда ширше, чем задавший вопрос. Имеют право, разве нет?
  • ага (16.01.08 06:27) [21]

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

    Ан нет, не совсем. Медленнее будет, чем только со стартерам, но отдельным для каждого потока
  • ага (16.01.08 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 использовать.
  • ага (16.01.08 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]
  • ага (16.01.08 08:35) [24]
    Поспешишь - людей насмешишь:( Фигню я написал в последних постах. Сейчас вернулся, посмотрел спокойно - полная фигня. Не будет остановки потоков в WaitForSingleObject (startEvent,INFINITE), эвент-то не сброшен. В общем, не обращай на это внимание.

    Быстрее всего будет при использовании CompletionPort или, если без порта, при отдельном стартере на каждый поток.
  • mt2 (16.01.08 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 стабильно повторяется. Надо будет разбираться, почему.
  • ага (17.01.08 07:27) [26]

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

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

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

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

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

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

    Да какой там нафиг копирайт:))) Три вызова апишных функций, завернутых в элементарный класс:)) Ну используй эти функции напрямую, без класса, никакого копирайта:)
  • mt2 (17.01.08 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 © (17.01.08 23:07) [28]

    > mt2   (17.01.08 22:20) [27]

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

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

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

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

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

    --
    Regards, LVT.
  • ага (18.01.08 18:54) [29]

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

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

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

    Опыт есть, но не в таких масштабах, чтобы можно было привести впечатляющие цифры. Однако выигрыш достаточно заметен, если конечно остальным кодом его не сожрать.
  • mt2 (18.01.08 19:08) [30]

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


    Ok. Спасибо!
 
Конференция "WinAPI" » Странный deadlock [D7, WinXP]
Есть новые Нет новых   [134431   +15][b:0.001][p:0.013]