Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Распараллеливание потока


Автор: eXa 19.7.2007, 04:09
Драсти!
Есть цикл, каторый построчно перебирает значения из текстового файла, требуется распараллелить этот цикл на заданное количество потоков, для ускарения процесса. Сколько про потки мне не объясняли и сколько я про них нечитал, всё равно не пойму как это сделать, приветствуется пример!
Код

var
count, i, a: integer;
text: string;
 begin
  Count:= Ls.Count
  for i:= 0 to Count do
  begin
   text:= Ls.Strings[i];
   a:=  StrToInt(text) + 1;
  end;

Автор: MetalFan 19.7.2007, 06:04
уууу дарагой!
пачитай умную книжку, да?! если ничего нипаймешь, значит праграммиравание нитвае!
з.ы. а еще сначала русский язык подучи. а то 2 ошибки в заголовке темы очень уж глаза режут.

Автор: W4FhLF 19.7.2007, 07:32
Распараллеливание в данном случае прироста к скорости не даст. 

Автор: Alexeis 19.7.2007, 09:28
eXa, специально для таких случаев добрый дядя Петрович написал неплохую обучающую статейку для освоения главных принципов работы с потоками http://forum.vingrad.ru/index.php?show_type=forum&showtopic=163841

И перевел целую книжку http://forum.vingrad.ru/index.php?show_type=forum&showtopic=60076

Автор: eXa 19.7.2007, 12:30
Цитата(MetalFan @ 19.7.2007,  06:04)
уууу дарагой!
пачитай умную книжку, да?! если ничего нипаймешь, значит праграммиравание нитвае!
з.ы. а еще сначала русский язык подучи. а то 2 ошибки в заголовке темы очень уж глаза режут.

Маё, маё, но вот патоки мне даюца труднавато...
ps. а тут форум литературного кружка разве, на ошипки так реагировать? O_o 

Цитата

eXa, специально для таких случаев добрый дядя Петрович написал неплохую обучающую статейку для освоения главных принципов работы с потоками http://forum.vingrad.ru/index.php?show_typ...howtopic=163841

И перевел целую книжку http://forum.vingrad.ru/index.php?show_typ...showtopic=60076


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

Автор: Alexeis 19.7.2007, 12:54
Цитата(eXa @  19.7.2007,  12:30 Найти цитируемый пост)
Петрович круче всех конешно и за перевод ему 100 плюсов, но 95% информации каторой там есть мне просто не нужно, да и на практике я лучше как то понимаю, если ковот не затруднит выложите исходничик, в катором можно задавать кол-во потоков

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

Автор: MetalFan 19.7.2007, 13:51
Цитата(Alexeis @  19.7.2007,  12:54 Найти цитируемый пост)
 Вот ты как раз тот начинающий сапер, который рвется на разминирование, так что мы тебя не зря не пускаем, так как многопоточность - это минное поле, причем там мины разных конструкций.  

во загнул) smile 

Автор: aktuba 19.7.2007, 14:01
Цитата

Маё, маё, но вот патоки мне даюца труднавато...
ps. а тут форум литературного кружка разве, на ошипки так реагировать? O_o 


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

Цитата

но 95% информации каторой там есть мне просто не нужно


Странная точка зрения. Представь - едешь на машине и знаешь только как руль крутить. Далеко уедешь? А как-же педали, коробка передач??? Про поворотники, фары, сигнал и многое другое - вообще лучше не упоминать. Да и про сапера пример отличный. Так что
Цитата

Маё, маё


явно не верно! Не твое это, если и за своей речью следить не можешь и программирование пытаешься по кусочкам изучать...

Автор: eXa 19.7.2007, 18:40
Мне это напоминает бабулек около подъезда, с богатым жизненым опытом... Да во многом вы правы, но нинад подстраивать всё под себя, есть много методов обучения тому то и тому то, да и вообще бывают исключения даже у сапёров smile 

aktuba я тебя не понимать! Как можно приравнивать язык на катором разговариваеш с программированием, это тоже самое как прировнять его к физике или математике. Cкажи это Энштейну, каторому в школе язык ну никак не давался! Да и вообще это форум, у многих свои привычки общения в нём, не забывай этого, и пожалуст нинад так критично в чём то поучать, это же самое настоящие лицемерие!

Ну если исходник никто мне дать ненамерен(я уже большего не прошу), то тему можно закрыть! Дальше ток оффтоп может быть, можно во флейм перекинуть там и поспорим, если интерестно :P

Автор: Romikgy 19.7.2007, 21:59
Цитата(eXa @  19.7.2007,  17:40 Найти цитируемый пост)
Как можно приравнивать язык на катором разговариваеш с программированием,

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

Автор: aktuba 21.7.2007, 14:39
Цитата

Да во многом вы правы, но нинад подстраивать всё под себя


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

Цитата

aktuba я тебя не понимать!


Очень жаль.

Цитата

Как можно приравнивать язык на катором разговариваеш с программированием, это тоже самое как прировнять его к физике или математике.


А учебники по физике или математике как написыны? Твоим стилем или общеразговорным? И как тебе будет более понятно и приятно читать книги, которые будут написаны твоим стилем???

Цитата

Да и вообще это форум, у многих свои привычки общения в нём, не забывай этого, и пожалуст нинад так критично в чём то поучать, это же самое настоящие лицемерие!


Вот именно!!! И надо уважать мнения других! Не я один тебе уже сделал замечание, а ты все по своему... Лицемерие - это не обращать внимание на других, т.е. в данном случае - это больше подходит тебе. Подумай над этим.

Цитата

Ну если исходник никто мне дать ненамерен


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

Автор: Elerond 21.7.2007, 14:50
Книги читай... есть такие как раз для новичков. Если надо могу сказать какая.  Там и про потоки есть.

Автор: eXa 23.7.2007, 04:11
Ребята это вы уже совсем про другое.. У меня же не свой язык общения или какой то сленг, а просто сокращения типа "ться" заменить на "ц" и само сабой есть какие то неумешленные ошибки каторые общий смысл предложения не меняют, причём хочу заметить что мозг воспринимает только первую и последнию букву в слове, а порядок между ними особова значения не имеет(если интересно гугл в помощь), да и учебники я писать не собираюсь, во всяком случии пока smile 

А терь по теме!

В этом случии я создаю 5 потоков?

Код

tnew = class(tthread)
protected
procedure execute; override;
end;

var
  new:array [0..4] of tnew;

procedure tnew.execute;
var
n: integer;
begin
n := 0;
while true do
begin
if n > 99 then
n:= 0;
form1.progressbar1.position := n;
inc(n)
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
begin
i:= 4;
new[i]:= tnew.create(true);
new[i].freeonterminate := true;
new[i].resume;
end;


Автор: aktuba 23.7.2007, 06:07
Цитата

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


Вот! Можешь же, когда хочешь  smile 

А по теме - нет, ты создаешь только один поток...

Автор: Alexeis 23.7.2007, 08:55
  Нужно вот это 
Код

new[i]:= tnew.create(true);
new[i].freeonterminate := true;
new[i].resume;

 в цикле вызывать, чтобы создать несколько потоков.

Автор: Демо 24.7.2007, 10:13
Цитата(W4FhLF @  19.7.2007,  07:32 Найти цитируемый пост)
Распараллеливание в данном случае прироста к скорости не даст. 


Даёт.

eXa, 

для решения твоей задачи возможны несколько подходов.

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

1.
- Для каждого потока выделяется диапазон строк из TStringList;
- строки при создании потока копируются для обработки, на время копирования TStringList блокируется для доступа из других потоков средствами синхронизации
- каждый поток обрабатывает свой список строк, затеп копирует обратно, на время копирования TStringList блокируется для доступа из других потоков средствами синхронизации

2. 
- Каждый поток обрабатывает строки из диапазона ПОСТРОЧНО. При этом исходный список блокируется на время копирования строки в поток, а затем обратно.

Вот примерный код для первого метода:

Код

unit ufMain;

interface

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

type

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

    TFirstThread=class(TThread)
    private
    FList: TStringlist;
    FMainList: TStringList;
    FCS: TCriticalSection;
    FB, FE: Integer;
  public
    constructor Create(List: TStrings; BIndex,EIndex: integer; aCS: TCriticalSection);
    destructor Destroy; override;
  protected
    procedure Execute; override;
    end;

var
  Form1: TForm1;
  CS: TCriticalSection;
implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
    CS := TCriticalSection.Create;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
    Memo1.Lines.LoadFromFile('c:\test.txt');
  TFirstThread.Create(Memo1.Lines,0,(Memo1.Lines.Count div 2),CS);
  TFirstThread.Create(Memo1.Lines,(Memo1.Lines.Count div 2)+1,Memo1.Lines.Count-1,CS);
end;

{ TFirstThread }

constructor TFirstThread.Create(List: TStrings; BIndex, EIndex: integer; aCS: TCriticalSection);
var
    i: integer;
begin
    inherited Create(True);
    FreeOnTerminate := True;
  FList := TStringList.Create;
  FMainList := TStringList(List);
  FCS := aCS;
  FB :=BIndex;
  FE := EIndex;
    FCS.Enter;
  try
    try
        for i := FB to FE do FList.Add(List[i]);
    except
     //Îáðàáîòêà îøèáêè
      Terminate;
    end;
  finally
    FCS.Leave;
  end;
  Resume;
end;

destructor TFirstThread.Destroy;
begin
    FList.Free;
  inherited;
end;

procedure TFirstThread.Execute;
var
    i: Integer;
begin
    for i := 0 to FList.Count-1 do
  begin
    //îáðàáîòêà ñòðîêè
    if Terminated then Exit;
  end;

    FCS.Enter;
  try
    try
        for i := 0 to FList.Count-1 do FMainList[i+FB] := FList[i];
    except
            //îáðàáîòêà îøèáêè
    end;
  finally
    FCS.Leave;
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
    CS.Free;
end;

end.


Автор: dumb 24.7.2007, 13:25
Цитата(Демо @  24.7.2007,  10:13 Найти цитируемый пост)
Цитата(W4FhLF @  19.7.2007,  07:32 Найти цитируемый пост)
Распараллеливание в данном случае прироста к скорости не даст.

Даёт.

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

Автор: Демо 24.7.2007, 14:34
Цитата(dumb @  24.7.2007,  13:25 Найти цитируемый пост)
эк безапелляционно. распараллеливание даст прирост только в случае небольшого файла и очень ресурсоемкой обработки каждой строки. в "нашем" же случае, я больше чем уверен, что обработка простейшая, а тормоза получаются из-за загрузки довольно объемного файла в стринг-лист.так что гораздо быстрее получится, если просто читать файл построчно...


безапелляционно, потому что знание - сила.

Ты упустил одну маленькую деталь - система может быть многопроцессорной.

Автор: dumb 24.7.2007, 15:05
Цитата(Демо @  24.7.2007,  14:34 Найти цитируемый пост)
Ты упустил одну маленькую деталь - система может быть многопроцессорной.

не упустил. при простой обработке и большом объеме файла основные тормоза - операция чтения с диска: тут кол-во процессоров никакой роли не сыграет. а вот расходы на загрузку в стринг-грид(в основном) и создание тредов - очень даже.

Автор: eXa 24.7.2007, 15:10
Спасиб +100 за помощь, поразбираюсь когда будет время, а в сабже я привёл только пример для примера потоков  smile  А конечной целью является ускорение отсылаемых данных на сервер, которые берутся из txt файла построчно и задаваемое кол-во потоков...

Автор: Демо 24.7.2007, 15:21
Цитата(dumb @  24.7.2007,  15:05 Найти цитируемый пост)
не упустил. при простой обработке и большом объеме файла основные тормоза - операция чтения с диска: тут кол-во процессоров никакой роли не сыграет. а вот расходы на загрузку в стринг-грид(в основном) и создание тредов - очень даже.


Размер файла не имеет значения. Пусть это 10Кб-файл или файл размером 10Гб.
Важна реализация.

Добавлено @ 15:24
Цитата(dumb @  24.7.2007,  15:05 Найти цитируемый пост)
большом объеме файла основные тормоза - операция чтения с диска


Опять же. Диск диску рознь.
При кэше размером в несколько мегабайт и быстрых современных дисках со скоростью в десятки Мб/сек скорость обработки данных очень даже будет иметь значение. 

Автор: eXa 26.7.2007, 08:22
А вот что делать, если  на форме есть TEdit который используеться в потоки, если TEdit находится в class(TForm) то поток его не видит Undeclared identifier: 'Edit1' а в  class(TThread)  тоже не поместишь...

Автор: Демо 26.7.2007, 08:41
eXa, 

1. Для использования VCL из отдельного потока в основном необходимо применять метод Synchronize;
2. При создании потока в конструктор можно передать ссылку на твой TEdit и использовать ее;

Код

TMyThread=class(TThread)
private
...
   FEdit: TEdit;

constructor TMyThread. Create(Ed: TEdit);
begin
   FEdit := Ed;


А дальше используешь просто как FEdit.Text, например.

Автор: aktuba 26.7.2007, 16:14
Цитата

А дальше используешь просто как FEdit.Text, например. 


Ай-ай!!! Это что-же, правила работы с потоками не для всех пишуться???? А если ты будешь этот эдит будет использоваться в нескольких потоках???? В корне неверный совет!!!

Автор: Демо 26.7.2007, 16:32
Цитата(aktuba @  26.7.2007,  16:14 Найти цитируемый пост)
Ай-ай!!! Это что-же, правила работы с потоками не для всех пишуться???? А если ты будешь этот эдит будет использоваться в нескольких потоках???? В корне неверный совет!!!


См. предыдущий пост.
Читай внимательнее, пожалуйста, прежде чем замечания делать.

Автор: W4FhLF 26.7.2007, 16:53
В случае, когда в системе один процессор/ядро оптимальное число потоков, для задачи, где НЕТ БЛОКИРУЮЩИХ ОПЕРАЦИЙ,  1 штука. Увеличение потоков даст прирост в случае, если в системе параллельно активно работают ещё несколько потоков(с таким же приоритетом). Но в этом случае будет лучше повысить потоку приоритет, нежели распараллеливать задачу. 

PS Одному из участников форума я доказывал справедливость моих слов по ПМ, с примерами и теоретическим обоснованием, он тоже был удивлён. 

Автор: Alexeis 26.7.2007, 16:57
Цитата(W4FhLF @  26.7.2007,  16:53 Найти цитируемый пост)
PS Одному из участников форума я доказывал справедливость моих слов по ПМ, с примерами и теоретическим обоснованием, он тоже был удивлён. 

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

Автор: W4FhLF 26.7.2007, 17:05
Alexeis, ещё раз перечитай моё сообщение. Обязательны 3 условия:
1. Одно ядро/процессор.
2. Отсутствие блокирующих операций.
3. Отсутствие активно расходующих процессорное время параллельных потоков других приложения, с тем же приоритетом(почти у любого из нас система работает в этом режиме практически постоянно).

Сейчас будут обоснования.

Добавлено @ 17:10
Код

var
  Form1: TForm1;
  ad,i,a:integer;
  ThID: array [0..1] of dword;
  by: byte;

function ThreadProc(): dword; stdcall;
begin
    asm
      push ecx
      mov ecx,$FFFFFFFF
      @f:
        inc ad
        xor byte ptr[by],$64
        add byte ptr[by],$46
        xor byte ptr[by],$54
        shl byte ptr[by],2
      loop @f
      pop ecx
    end;
    ExitThread(0);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
    ThID[0] :=CreateThread(nil,128*1024,@ThreadProc,nil,0,ThID[0]);
    a := GetTickCount();
    WaitForMultipleObjects(1, @ThID, FALSE, INFINITE);
    ShowMessage(IntToStr(GetTickCount() - a));
end;



Что делает процедура ThreadProc можешь не вникать, суть в том, что там написан цикл, на ассемблере, с числом иттераций FFFFFFFF(4294967295). Наилучшее время выполнения: 56.766 сек. 

Далее запускаю такой пример:

Код

var
  Form1: TForm1;
  ad,i,a:integer;
  ThID: array [0..1] of dword;
  by: byte;

implementation

{$R *.dfm}

function ThreadProc(): dword; stdcall;
begin
    asm
      push ecx
      mov ecx,$7FFFFFFF
      @f:
        inc ad
        xor byte ptr[by],$64
        add byte ptr[by],$46
        xor byte ptr[by],$54
        shl byte ptr[by],2
      loop @f
      pop ecx
    end;
    ExitThread(0);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
    ThID[0] :=CreateThread(nil,128*1024,@ThreadProc,nil,0,ThID[0]);
    ThID[1] :=CreateThread(nil,128*1024,@ThreadProc,nil,0,ThID[1]);
    a := GetTickCount();
    WaitForMultipleObjects(2, @ThID, FALSE, INFINITE);
    ShowMessage(IntToStr(GetTickCount() - a));
end;


Как видишь, два потока, в каждом будет выполняться цикл с числом иттераций 7FFFFFFF(2147483647), в общей сложности два потока выполнят 7FFFFFFF*2 = FFFFFFFE иттераций. Измерил и получил наилучшее время выполнения 56.046 сек. 

Учитывая, что код обоих потоков сжох? можно предположить, что они хорошо закешированы и поэтому выполняются так же быстро. 
Так же не учитывается погрешность с которой измеряет время GetTickCount, она составляет ~60 мс.  
Программы запускал по 5 раз каждую, в качестве результат брал наименьшее значение, т.к. оно является самым верным(не среднее).

Счастливым обладателым n-ядерных процессоров предлагаю прочесть справку по SetThreadAffinityMask и, грамотно распараллелив потоки, сделать замер. 

PS AMD Athlon AM2 3500+

Автор: Alexeis 26.7.2007, 17:13
 Ааа сори, не нужно, все и так понятно, вот только условие "НЕТ БЛОКИРУЮЩИХ ОПЕРАЦИЙ" - почти утопия. В компе много устройств и работают они не всегда достачно быстро чтобы обеспечить друг друга данными, из-за чего обычно процессор не используется на 100%, так что если грамотно раскидать на потоки операции, то прирост будет. 

Автор: Демо 27.7.2007, 08:42
Цитата(W4FhLF @  26.7.2007,  17:05 Найти цитируемый пост)
Счастливым обладателым n-ядерных процессоров предлагаю прочесть справку по SetThreadAffinityMask и, грамотно распараллелив потоки, сделать замер. 


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

Добавлено через 1 минуту и 12 секунд
Цитата(W4FhLF @  26.7.2007,  16:53 Найти цитируемый пост)
 для задачи, где НЕТ БЛОКИРУЮЩИХ ОПЕРАЦИЙ,


Почти вырожденный случай.

Автор: eXa 27.7.2007, 21:28
Удалось передать VCL первым методом...
Код

procedure TNew.InMemo;
begin
textin:= Form1.FMemo.Text;
end;


Код

begin
CS.Enter;
    Synchronize(InMemo);
......
 CS.Leave


Возникают вопросы...
1. Можно передать TMemo без дополнительной процедуры и без конструктора?
2. Если я создаю больше одного потока, как будет вести себя код?

Автор: MetalFan 28.7.2007, 17:30
1. вопрос непонятен. куда передать? какой конструктор?
2. если код написан правильно, то программа будет себя вести хорошо)

з.ы. нафиг критическую секцию припаял? у тебя и так код в Synchronize в основном потоке будет работать

Автор: Демо 28.7.2007, 21:15
eXa, 
В моём примере критическую секцию надо убрать и использовать только Synchronize, так как обновляется VCL-компонент.


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)