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


Автор: Roman1 17.2.2010, 15:40
Всем доброго времени суток. Прошу помочь с такой задачей. Программа производит математические вычисления нескольких функций от времени. Берется приращение времени dt,  последовательно вычисляются независимые друг от друга функции, затем  дополнительно обрабатываются и в цикле переходим к новому t+dt Пробую пустить вычисления этих функций по потокам для ускорения работы программы, так как формулы довольно громоздкие. Получилось следующее. Потоки обрабатывают глобальные переменные. После взятия нового значения времени потоки создаются через create, потом ждем их завершения через WaitFor, уничтожаем через Free, ну и обрабатываем измененыые глобальные переменные. Программа стала работать еще дольше, так как формулы большие и времени уходит много на создание потоков. А запустить методы Execute этих потоков без их уничтожения не получается. Т.е. если при запуске программы создать потоки, а  потом на каждое приращение времени вызывать их через Execute и ждать завершения, то они работают только один раз,  вызов Execute на следующем временном шаге никаких действий не производит. Читал, что нужно в этом случае применять Suspend, а потом Execute. Но в методах Execute каждого потока никаких ожиданий или циклов нет. После завершения вычислений поток, видимо, прекращает работу. Пробовал после WaitFor писать suspend, получается ошибка.
Прошу подсказать, как запускать методы Execute потоков в данном случае. Или какое-либо другое решение по ускорению вычислений.

Автор: bems 17.2.2010, 18:37
Не понял описанную структуру программы, но первое что бросается в глаза: метод Execute не предназначен для прямого вызова. Он вызывается классом автоматически при создании потока (если CreateSuspended = False) или при вызове Resume (если CreateSuspended = True). При вызове  Suspend поток останавливается где-то внутри метода Execute и для продолжения тебе нужно вызвать Resume

Автор: kami 17.2.2010, 18:57
Цитата(Roman1 @  17.2.2010,  15:40 Найти цитируемый пост)
 После завершения вычислений поток, видимо, прекращает работу. 

само собой. Выход из метода Execute это прекращение жизни потока.

Один из вариантов выхода - работа через сообщения:
Код

const
  msgThreadClose=WM_USER+1;
  msgDoCalculate=WM_USER+2;
  msgCalcComplete=WM_USER+3;

TMyThread.Create(MainFormHandle:THWND);
begin
  FMainFormHandle:=MainFormHandle; // запоминаем хендл нашей формы
  inherited Create(False);
end;

TMyThread.Execute:
var
  Msg: TMSG;
  i: integer;
begin
  { Create message queue }
  PeekMessage(msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
  // инициализируем все, что нужно для работы потока
  while not Terminated and GetMessage(msg, 0, 0, 0) do // выбираем сообщения в цикле
    begin
      if msg.hwnd = 0 then // это сообщение потоку, а не окну, созданному в его контексте
        case msg.Message of
          msgThreadClose:
            begin
              Terminate;
              break;
            end;
          msgDoCalculate:
            begin
              DoCalculate; // производим вычисления
              PostMessage(FMainFormHandle, msgCalcComplete, 0, 0); // сообщаем об этом основному потоку
            end;
        end
      else
        DispatchMessage(msg);
    end;
  // высвобождаем все ресурсы, созданные при входе в Execute 
end;


В главной (или какой-нибудь) форме:
Код

TMainForm=class(TForm);
private
  procedure CalculationComplete(var MSG:TMessage); message msgCalcComplete;

TMainForm.OnCreate(...);
begin
  FMyThread:=TMyThread.Create(Handle);
end;

TMainForm.OnNeedCalculate(...);
begin
  // запускаем поток на новый виток вычислений.
  // при этом нужно понимать, что новые вычисления не начнутся, пока не закончатся старые,
  // хоть сколько посылай сообщения - они все будут поставлены в очередь потока на обработку.
  PostThreadMessage(FMyThread.ThreadID, msgDoCalculate, 0, 0);
end;

TMainForm.CalculationComplete(var MSG:TMessage);
begin
  // здесь мы окажемся, когда поток закончит вычисления и до нашей формы дойдет его сообщение
  // об окончании вычислений.
end;

TMainForm.OnDestroy(..)
begin
  PostThreadMessage(FMyThread.ThreadID, msgThreadClose, 0, 0);
  // 
end;

Автор: Roman1 18.2.2010, 08:48
Прошу прощения, я ошибся когда писал тему. Конечно, я вызывал Execute через Resume. По поводу работы через сообщения -спасибо. Сейчас буду разбираться. 
Структура программы такая. В каждом потоке объявлены несколько функций, во всех потоках разные. Перед началом вычислений на очередном временном шаге создаем все эти потоки через Create(false), тут же в главном потоке ждем их завершения и уничтожаем. Обрабатываем измененные потоками глобальные переменные. Переходим к следующему шагу и так далее. Как я уже говорил, иначе не получается. Без уничтожения и повторного создания потока он через Resume не запускается.

Автор: Beltar 18.2.2010, 11:54
Цитата

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


Какое ускорение? Откуда? А если процессор всего один?

Честно говоря, у меня вообще есть сомнения, что вычисления всей твоей математики для одного конкретного t способны занять компьютер, даже 10-летней давности, на время большее выделяемого потоку.
И вообще что именно требуется. Обеспечить управляемость программы пока идут вычисления? Ну создай один поток, создай в нем одну рабочую ф-ию, которую будет вызывать Execute. В Execute оформи бесконечный цикл с выходом по Terminated. Набросай по ф-ии if Terminated then Exit. После выхода из рабочей ф-ии if not Terminated then Suspend; И предусмотри в дополнение к Terminated еще один флажок, по которому тоже будет осуществляться выход из рабочей ф-ии, так чтобы поток после каждой остановки не убивать. У меня это отлично работало.

А вообще http://forum.vingrad.ru/forum/topic-60076.html

Автор: Roman1 18.2.2010, 13:56
Требуется ускорить работу. Формулы действительно большие - гиперболические синусы, косинусы и т.п. (занимают много времени, я проверял, заменял на упрощенный вариант, где плюс, минус, умножить, равно. Работает гораздо быстрее, но нет нужной точности) Шаг по времени  для нормальной точности расчетов приходится брать 1 микросекунда. Быстродействие зависит от количества объектов, которые надо обсчитать. По каждому объекту несколько глобальных переменных. Получается, если обычно объектов штук 8, то расчет до времени 15-20 секунд идет часа четыре. Правда, там по ходу строятся графики, но точки ставятся каждые 5000 шагов. Можно больше, можно меньше. В основном скорость зависит от расчетов. 
По поводу процессоров - да, на одном не ускорится.  Но сейчас кругом многопроцессорные машины, а программу, если получится сделать, предполагается распространять.
По поводу метода спасибо. Уже сижу пробую что-нибудь сделать

Автор: Roman1 18.2.2010, 17:30
Всем спасибо. Ответы на мои вопросы понятны, что-то начало получаться. Ссылка тоже очень пригодилась. Тема закрыта

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