Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > ОС и железо
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 17.12.2013, 19:44
mustimur mustimur вне форума
Прохожий
 
Регистрация: 20.11.2013
Сообщения: 17
Версия Delphi: Delphi xe4
Репутация: 10
По умолчанию Многопоточность

Доброго времени суток.

Попробовал написать многопоточную программу (до этого нужды не было в это лезть) и как говорится первый блин комом.
Суть проблемы в следующем программа производит значительное число однотипных расчетов, по сути которые можно обсчитывать параллельно.
С помощью класса TThread создается программой несколько потоков (число потоков определяется пользователь программы) каждый из которых берет на себя равную часть расчетов. Потоки формируются и отрабатывают правильно расчет (проверял) однако скорость осталась прежней, что и с одним потоком. В диспетчере задач показано, что программе присвоен один процесс и загрузка процессора всегда 25%, не зависимо от числа потоков.
Что я не правильно понял?
Ответить с цитированием
  #2  
Старый 17.12.2013, 20:17
Аватар для PhoeniX
PhoeniX PhoeniX вне форума
Always hardcore!
 
Регистрация: 04.03.2009
Адрес: СПб
Сообщения: 3,239
Версия Delphi: GCC/FPC/FASM
Репутация: 62149
По умолчанию

Кусок кода сказал бы больше, чем 80% вашего сообщения
__________________
Оставайтесь хорошими людьми...
VK id2634397, ds [at] phoenix [dot] dj
Ответить с цитированием
  #3  
Старый 17.12.2013, 20:32
mustimur mustimur вне форума
Прохожий
 
Регистрация: 20.11.2013
Сообщения: 17
Версия Delphi: Delphi xe4
Репутация: 10
По умолчанию Код специфичный)))

Цитата:
Сообщение от PhoeniX
Кусок кода сказал бы больше, чем 80% вашего сообщения
Я не думаю что легче будет, но код вот:
Код:
itc:=sedt.Value;// Определяет число параллельный потоков
setlength(ther,itc); 
for it := 1 to itc do
  begin
  ther[it-1]:= Rasch.Create(true); //Rasch - мой класс TThread
  ther[it-1].FreeOnTerminate := true;
  if it =1 then ther[it-1].kss:=0 else ther[it-1].kss:=(it-1)*(kg mod itc)+1;// определяет с какого элемента считать начать текущему потоку
  if it=itc then ther[it-1].ke:=kg else ther[it-1].ke:=(it)*(kg mod itc); // определяет каким элементом закончить счет текущему потоку
  ther[it-1].proc:=rasc1; // определяет процедуру для расчета
  ther[it-1].start;
end;
  enp:=false;
  while (enp=false) do //Ожидание окончание всех потоков
  begin
  enp:=ther[0].Finished;
  if itc>1 then for it := 2 to itc do enp:=(enp and ther[it-1].Finished);
  end;

Чтобы было понятнее приложу код класса Rasch
Код:
unit Rasc;

interface

uses
  System.Classes;

type
  ProcRasc = procedure(kstr, kend:integer);
  Rasch = class(TThread)
  private
    { Private declarations }
  public
  
  proc: ProcRasc;
  kss,ke: integer;

  protected
    procedure Execute; override;
  end;


implementation


{ Rasch }

procedure Rasch.Execute;
begin
  proc(kss,ke);
end;

end.

Последний раз редактировалось mustimur, 17.12.2013 в 20:38.
Ответить с цитированием
  #4  
Старый 17.12.2013, 20:39
Аватар для PhoeniX
PhoeniX PhoeniX вне форума
Always hardcore!
 
Регистрация: 04.03.2009
Адрес: СПб
Сообщения: 3,239
Версия Delphi: GCC/FPC/FASM
Репутация: 62149
По умолчанию

Код:
ther[it-1].start;
Попробуй заменить на
Код:
ther[it-1].resume;
В остальном всё должно работать...

P.S. А, ну и процедуры расчёта должны быть, как говорится, ThreadSafe - думаю, это объяснять не надо?
P.P.S.
Цитата:
Сообщение от mustimur
Я не думаю что легче будет
Мы тут экземпляры класса TProgrammer, и у нас метод Understand заточен под тип сообщений TCode
P.P.P.S. Ну и число потоков лучше рассчитывать из числа процессоров/ядер, а не из объёма работ.
__________________
Оставайтесь хорошими людьми...
VK id2634397, ds [at] phoenix [dot] dj

Последний раз редактировалось PhoeniX, 17.12.2013 в 20:45.
Ответить с цитированием
  #5  
Старый 17.12.2013, 21:03
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Небольшое дополнение.
Код:
while (enp=false) do //Ожидание окончание всех потоков
  begin
  enp:=ther[0].Finished;
  if itc>1 then for it := 2 to itc do enp:=(enp and ther[it-1].Finished);
  end;
Было бы логичней
Код:
do
  enp = true;
  for it := 0 to itc - 1 do
    enp := enp and ther[it].Finished;
while (enp = false)
Ну и IF-ы в составлении потоков можно и убрать:
Код:
ther[it-1].kss:=(it-1)*(kg mod itc);
ther[it-1].ke:=(it)*(kg mod itc) - 1;
После чего этот цикл тоже перевести на 0 to itc-1 и повысить читаемость, убрав все -1 и добавив в одном месте +1.
Код:
for it := 0 to itc-1 do
  begin
  ther[it]:= Rasch.Create(true);
  ther[it].FreeOnTerminate := true;
  ther[it].kss:=it*(kg mod itc);
  ther[it].ke:=(it + 1)*(kg mod itc) - 1;
  ther[it].proc:=rasc1;
  ther[it].start;
end;
Проще читается как-то.
ЗЫЖ если я правильно помню, при выставленном FreeOnTerminate поток по завершении самоуничтожается, а ты после этого его флаги читаешь. Не гут.
Цитата:
Сообщение от Спеки Embarcadero
Warning: When FreeOnTerminate is true, the Execute method may run and then free the thread before your application can execute the next line of code. Thus, you should not call any methods of the thread object when FreeOnTerminate is true unless you create the thread in a suspended state. FreeOnTerminate is best left for threads that simply perform a task and then naturally terminate. If you want to communicate with the thread or otherwise interact with it, including telling it when to terminate, FreeOnTerminate should never be used.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 17.12.2013 в 21:14.
Ответить с цитированием
  #6  
Старый 17.12.2013, 21:50
mustimur mustimur вне форума
Прохожий
 
Регистрация: 20.11.2013
Сообщения: 17
Версия Delphi: Delphi xe4
Репутация: 10
По умолчанию

Цитата:
Сообщение от PhoeniX

Мы тут экземпляры класса TProgrammer, и у нас метод Understand заточен под тип сообщений TCode .

Шутку оценил
Код:
ther.resume
пробовал результаты теже....
Ответить с цитированием
  #7  
Старый 17.12.2013, 22:01
mustimur mustimur вне форума
Прохожий
 
Регистрация: 20.11.2013
Сообщения: 17
Версия Delphi: Delphi xe4
Репутация: 10
Вопрос

Вы правы Ваш код изящней!!!

Цитата:
Сообщение от Bargest


Проще читается как-то.
ЗЫЖ если я правильно помню, при выставленном FreeOnTerminate поток по завершении самоуничтожается, а ты после этого его флаги читаешь. Не гут.
так я надеюсь на его уничтожение по завершении расчетов и жду этого, когда все потоки уничтожатся, чтобы продолжить... А что не так?

Но главный вопрос при этом коде ни скорость счета ни загрузка процессора не зависит от числа потоков. Процессор всегда загружен на 25% (похоже на одно ядро)!!! Почему??????
Ответить с цитированием
  #8  
Старый 17.12.2013, 22:20
Аватар для Bargest
Bargest Bargest вне форума
Профессионал
 
Регистрация: 19.10.2010
Адрес: Москва
Сообщения: 2,390
Версия Delphi: XE3/VS12/FASM
Репутация: 14665
По умолчанию

Цитата:
Сообщение от mustimur
Простите комп повис как удалить повторы?
Никак. Попроси модеров (Admin, M.A.D.M.A.N, lmikle). Пока отредактируй и измени текст на < удалено > (т.к. минимум 10 символов).
Цитата:
А что не так?
Поток завершился, объект удалился, а потом ты обращаешься к его полю Finished, т.е. к полю несуществующего объекта. Работать скорее всего будет в большинстве случаев, но только благодаря тому, что память не зануляется при удалении.
Цитата:
(похоже на одно ядро)!!! Почему??????
Приложи еще код расчетов.
ЗЫЖ черт, совсем забыл, что в делфе нет do-while... Надо поменять на repeat-until и условие поставить обратное.
__________________
jmp $ ; Happy End!
The Cake Is A Lie.

Последний раз редактировалось Bargest, 17.12.2013 в 22:32.
Ответить с цитированием
Этот пользователь сказал Спасибо Bargest за это полезное сообщение:
mustimur (18.12.2013)
  #9  
Старый 18.12.2013, 03:07
Аватар для poli-smen
poli-smen poli-smen вне форума
Профессионал
 
Регистрация: 06.08.2012
Адрес: Кривой Рог
Сообщения: 1,791
Версия Delphi: Delphi 7, XE2
Репутация: 4415
По умолчанию

Чтобы цикл ожидания потоков не напрягал процессор почём зря, нужно в этом цикле сделать небольшую задержку:
Код:
repeat
  Sleep(100); // Теоретически достаточно даже Sleep(0)
  enp := True;
  for it := 0 to itc - 1 do
    enp := enp and ther[it].Finished;
until enp;
Кроме того вместо самопального цикла ожидания можно использовать стандартную WaitForMultipleObjects которая умеет ожидать до 64 объектов синхронизации одновременно.

И да, при "FreeOnTerminate:=True" неправомерно выполнять обращения к объекту этого потока из другого потока, так как объект к этому времени уже может быть разрушен (вследствие завершения выполнения потока). Если необходимо обращаться к объекту потока из другого потока нужно делать "FreeOnTerminate:=False", но тогда и самостоятельно разрушать объекты потоков (или возложить эту миссию на объект класса TObjectList из модуля Contnrs).
Если же делать "FreeOnTerminate:=True", то так как к такому объекту потока, нельзя обращаться из другого потока - поток должен сам как-то оповестить о своём завершении. Например можно объявить глобальную переменную-счётчик потоков. При создании нового потока увеличивать эту переменную, а каждый поток перед своим завершением должен уменьшать эту переменную (обязательно через Synchronize или InterlockedDecrement), тогда в цикле достаточно просто проверять не достигло ли значение этой переменной нуля:
Код:
var
  NumThr: Integer = 0; // Это будет счётчик запущенных потоков
.....
begin
.....
  for it := 1 to itc do
  begin
    ther[it-1] := Rasch.Create(True);
    Inc(NumThr); // Увеличиваем количество созданных потоков на единицу
    ther[it-1].FreeOnTerminate := True; // Авторазрушаемый объект потока
.....
    ther[it-1].Resume;
  end;

  while NumThr > 0 do Sleep(100); // Ожидаем завершения всех потоков
.....
end;
.....

.....
procedure Rasch.Execute;
begin
  proc(kss, ke);
  InterlockedDecrement(NumThr); // Перед завершением потока уменьшаем количество
                                // запущенных потоков, а объект потока сам разрушится
                                // благодаря FreeOnTerminate := True
end;
Ответить с цитированием
Этот пользователь сказал Спасибо poli-smen за это полезное сообщение:
mustimur (18.12.2013)
  #10  
Старый 18.12.2013, 15:00
mustimur mustimur вне форума
Прохожий
 
Регистрация: 20.11.2013
Сообщения: 17
Версия Delphi: Delphi xe4
Репутация: 10
Хорошо

Цитата:
Сообщение от poli-smen
Если же делать "FreeOnTerminate:=True", то так как к такому объекту потока, нельзя обращаться из другого потока - поток должен сам как-то оповестить о своём завершении. Например можно объявить глобальную переменную-счётчик потоков. При создании нового потока увеличивать эту переменную, а каждый поток перед своим завершением должен уменьшать эту переменную (обязательно через Synchronize или InterlockedDecrement), тогда в цикле достаточно просто проверять не достигло ли значение этой переменной нуля:
Код:
var
  NumThr: Integer = 0; // Это будет счётчик запущенных потоков
.....
begin
.....
  for it := 1 to itc do
  begin
    ther[it-1] := Rasch.Create(True);
    Inc(NumThr); // Увеличиваем количество созданных потоков на единицу
    ther[it-1].FreeOnTerminate := True; // Авторазрушаемый объект потока
.....
    ther[it-1].Resume;
  end;

  while NumThr > 0 do Sleep(100); // Ожидаем завершения всех потоков
.....
end;
.....

.....
procedure Rasch.Execute;
begin
  proc(kss, ke);
  InterlockedDecrement(NumThr); // Перед завершением потока уменьшаем количество
                                // запущенных потоков, а объект потока сам разрушится
                                // благодаря FreeOnTerminate := True
end;

Спасибо Вы как всегда правы
Ответить с цитированием
  #11  
Старый 18.12.2013, 17:33
mustimur mustimur вне форума
Прохожий
 
Регистрация: 20.11.2013
Сообщения: 17
Версия Delphi: Delphi xe4
Репутация: 10
Смущение

Цитата:
Сообщение от Bargest
Поток завершился, объект удалился, а потом ты обращаешься к его полю Finished, т.е. к полю несуществующего объекта. Работать скорее всего будет в большинстве случаев, но только благодаря тому, что память не зануляется при удалении.

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

Цитата:
Finished is True when the thread execution is completed or terminated, and False otherwise. This property is read-only.

Т.е. оно True когда поток закончен или уничтожен..... Или я поть не правильно что-то понял?
Ответить с цитированием
  #12  
Старый 18.12.2013, 18:20
Аватар для poli-smen
poli-smen poli-smen вне форума
Профессионал
 
Регистрация: 06.08.2012
Адрес: Кривой Рог
Сообщения: 1,791
Версия Delphi: Delphi 7, XE2
Репутация: 4415
По умолчанию

Цитата:
Сообщение от mustimur
Однако в свое оправдание приведу цитату из официального хелпа (он меня и направил на ложный путь порока: обращения к мертвым потокам):
Цитата:
Finished is True when the thread execution is completed or terminated, and False otherwise. This property is read-only.
Т.е. оно True когда поток закончен или уничтожен..... Или я поть не правильно что-то понял?
Да. Понял не правильно. Поток (Thread) и объект потока (т.е. экземпляр класса TThread) это вещи совершенно разные. С потоками можно работать и не используя объекты класса TThread, используя вместо этого стандартные API-функции CreateThread, ResumeThread и т.д. Потоки это обычная возможность Windows (просто обычная процедура выполняющаяся параллельно), а TThread это разработка Borland (теперь это Embarcadero) - класс, на основе которого создаются объекты. Уничтожен может быть объект потока (т.е. класса TThread), а поток он не уничтожается, а просто завершается. Вот при "FreeOnTerminate:=True" объект класса TThread саморазрушится когда параллельный поток завершится, а при "FreeOnTerminate:=False" объект будет жить и хранить все свои данные и после завершения работы потока - такой объект нужно разрушать самостоятельно (Free) чтобы не было утечек памяти.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 18:51.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter