Форум программистов, компьютерный форум, киберфорум
krapotkin
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
Блог. Двадцать пять лет Делфи-практики

В этом блоге я буду публиковать ответы на вопросы, которые постоянно приходится повторять на форуме.
Здесь можно это сделать более развернуто и спокойно.

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

Начав с Делфи-2 двадцать пять лет назад, я прошел все версии, испробовал массу технологий, включая работу с БД, с графикой DirectX, связью с серверами и интернетом, разработку на Андроид и IOS, и многое, многое другое.
________________________________________ ________________________________________ ____
P.S. все, о чем здесь написано, всего лишь измышления из головы.
совпадения с реальными людьми и фактами случайны.

Про потоки

Запись от krapotkin размещена 13.08.2017 в 10:03
Показов 32985 Комментарии 59
Метки delphi

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

Сама концепция потоков проста. Вы можете мыслить их как корабль, отправляющийся в дальнее плавание. На Марс, например. Вроде как и можно устроить сеанс связи с Хьюстоном, но это геморрой, поэтому нужно на корабль погрузить все до старта, и только после возврата вы сможете разобрать трофеи. Лучше всего - запустить и забыть. Даже если вы запустите 20 кораблей, то возвращаться они будут по одному. И на каждом будет написан его бортовой номер, чтобы вы могли их отличать.
Как-то так.
И точно потоки не помогут вам ничего рисовать на экране или двигать компоненты. Зато они могут все рассчитать для этого, создать битмапы и заполнить какие-то структуры данных. А вы уже в главном потоке все это будете использовать для вывода на экран.

Часто путают поток ОС с классом TThread
Это не одно и то же. TThread призван запомнить все данные, нужные в потоке, запустить поток в операционной системе, выполнить действие в нем и вернуть результат.

Жизнь потока состоит из трех этапов
1. создание и инициализация (это еще не поток с т.з. ОС)
2. запуск настоящего потока (вот тут настоящий поток ОС работает)
3. окончание работы потока
Обратите внимание! В пунктах 1 и 3 работа идет в главном потоке, там, где формы и пользовательский ввод.
Только 2-я часть уходит в автономное плавание, забирая с собой все переменные, которые вы ей насовали с собой в части 1

Начнем
1. Нужно создать своего наследника TThread, имеющего все нужные поля. Заполнить эти поля. Запустить поток.
Примечание.
Все дополнительные классы выносите в дополнительные юниты! Пусть класс TMyThread будет жить в UMyThread.pas
UMyThread мы должны прописать в uses у формы, откуда будет запускать свои потоки.
Юнит UMyThread ничего о форме знать НЕ ДОЛЖЕН!

2. Самая стандартная задача - сходить в потоке в интернет и что-то скачать. Результат вернуть и использовать в форме
2.1. Залогиниться где-то один раз.
2.2. Скачать со 100 страниц информацию и опять же что-то с ней сделать
Разберем задачу 2.2. Она посложнее

Лирическое отступление
Класс TThread имеет замечательное свойство FreeOnTerminate. Если установить его в true, то не нужно будет хранить ссылку на созданный объект, чтобы потом удалить его вручную. Создали, запустили и забыли о нем. Память освободится сама. Сделать это лучше всего, переопределив конструктор нашего наследника, где и вписать FreeOnTerminate := true;
В том же конструкторе мы должны вызвать конструктор предка - класса TThread - с параметром true
Это значит, он не запустится сразу же, а даст нам сначала заполнить поля объекта, а мы потом его запустим командой Resume. (В свежих версиях Delphi вместо Resume нужно вызывать Start)

3. Главное. Как мы получим данные от потока?
У класса TThread есть обработчик OnTerminate. Если на него назначить нашу собственную процедуру, то она будет вызываться после Execute для каждого потока. Это удобно. Дважды удобно то, что OnTerminate работает уже в главном потоке и не надо ничего делать для синхронизации, можно прямо писать в компоненты, например выводить в мемо, или заполнять структуры данных, не боясь, что другие потоки тоже лезут туда. Все синхронизировано по факту. Поэтому это лучший метод возврата результата из потока.
Итак. Объявим метод формы с любым именем, лишь бы параметры были Sender: TObject
Delphi
1
2
3
4
5
6
7
procedure TMainForm.ThreadTerm(Sender: TObject);
var th:TMyThread absolute Sender;
begin
  // th - это наш вернувшийся из плавания поток. можно использовать все его поля
  // например если мы сделаем классу поле Answer, то здесь можно будет вывести его в Memo
  memo1.lines.add(th.answer);
end;
Для красоты и простоты переопределим конструктор нашего наследника, чтобы можно было указывать этот метод
Delphi
1
2
3
4
5
6
constructor TMyThread.Create(ATermProc:TNotifyEvent);
begin
  inherited Create(true);
  FreeOnTerminate:=true;
  OnTerminate:=ATermProc;
end;
соответственно создание потока с указанием ThreadTerm как обработчика OnTerminate:
Delphi
1
2
3
4
5
procedure TMainForm.RunThread();
var th:TMyThread;
begin
  th:=TMyThread.Create(ThreadTerm);
end;
Примечание.
Рекомендуется не заполонять систему своими потоками. 3000 потоков вашей программы почти с гарантией поставят систему на колени. Поэтому одновременно запустим N потоков. После окончания каждого одного будем запускать один следующий, пока не стартуем нужное нам количество. При этом активно всегда будет не более N потоков.

Итак, сценарий.
Запустить N потоков.
При завершении - проверять, нужно ли запустить еще, или нет.
Также проверять, если все потоки завершены, то подвести итог.
Кнопку запуска потоков сделаем неактивной до времени, когда закончится последний поток, чтобы нельзя было запустить процесс еще раз.
Реализация
Кликните здесь для просмотра всего текста
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
unit UMainForm;
 
interface
 
uses
  classes, types, forms, Controls, StdCtrls, Dialogs, SysUtils;
 
type
  TMainForm = class(TForm)
    bStart: TButton;
    m1: TMemo;
    procedure bStartClick(Sender: TObject);
  private
    procedure Log(const s: string);
    procedure ThreadTerm(Sender: TObject);
  public
    Started:integer;
    Finished:integer;
    procedure RunThread();
  end;
 
var
  MainForm: TMainForm;
 
implementation
 
uses
  UMyThread;
 
 
const
  MAX_THREADS=100;
  SAME_TIME=25;
 
{$R *.dfm}
 
 
 
 
{ TMainForm }
 
 
procedure TMainForm.Log(const s:string);
begin
  m1.Lines.Add(s);
end;
 
procedure TMainForm.ThreadTerm(Sender: TObject);
var
  th:TMyThread absolute Sender; // очень древняя магия. обозначает th:=TMyThread(Sender)
begin
  log(th.URL + ':'+th.answer);
  inc(Finished);
  if Started<MAX_THREADS then
    RunThread;
  if Finished=MAX_THREADS then
  begin
    log('===========================');
    log('Работа окончена');
    bStart.Enabled := true;
  end;
end;
 
procedure TMainForm.bStartClick(Sender: TObject);
var
  i: Integer;
begin
  bStart.enabled:=false;
  for i := 0 to SAME_TIME-1 do
    RunThread;
end;
 
procedure TMainForm.RunThread;
var
  th:TMyThread;
begin
  inc(Started);
  th:=TMyThread.Create('URL '+inttostr(Started), ThreadTerm );
  Log('запущен '+th.url);
  th.Resume;
end;
 
end.

Кликните здесь для просмотра всего текста
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
unit UMyThread;
 
interface
uses Classes;
type
  TMyThread=class(TThread)
  public
    URL:string;
    answer:string;
    procedure Execute;override;
    constructor Create(AURL:string; TermProc:TNotifyEvent);
  end;
 
implementation
uses Sysutils;
 
{ TMyThread }
 
constructor TMyThread.Create(AURL:string; TermProc:TNotifyEvent);
begin
  inherited Create(true);
  URL := AURL;
  FreeOnTerminate := true;
  OnTerminate := TermProc;
end;
 
procedure TMyThread.Execute;
var g:TGUID;
begin
  // предположим, что здесь мы сходили в интернет и забрали с адреса URL
  // что-то очень нужное. Ответ поместили в Answer
  // сейчас просто генерируем уникальный ответ
  CreateGuid(g);
  Answer:=g.ToString;
  // сымитируем, что все потоки получат ответ с разной скоростью
  Sleep(Random(50));
end;
 
end.


Часто задают вопрос, что делать, пока потоки выполняются. И заводят какие-то безумные циклы
Delphi
1
while true and Potoki<100 do  Application.ProcessMessages;
Это ужасно! Правильный ответ - ничего! Ваше "ничего не делать" для программы это куча нужной работы:
отслеживание мыши, клавиатуры, перерисовка себя любимой, реагирование на всякие сообщения системы.
Это ее нормальное состояние.
Что-либо еще(!) делать она будет, когда возвращается поток.
Например, как в данном случае, когда все потоки завершены, мы выводим запись в мемо.


UPD
Давайте рассмотрим еще несколько вопросов

Пример 1
Мы запустили поток. Он занят чем-то продолжительным но не единым действием, а в цикле.
Кликните здесь для просмотра всего текста
например
Delphi
1
2
3
4
5
while i<1000 do
begin
  doSomething();
  inc(i);
end;
для остановки потока в классе TThread есть простой флаг Terminated
тогда модифицируем условие
Delphi
1
2
3
4
5
while (i<1000) and (not Terminated) do
begin
  doSomething();
  inc(i);
end;
и соответственно, если мы не выбросим ссылку на созданный поток,
Delphi
1
2
th:=TMyThread.Create(true);
th.Resume;
...мы можем сделать
Delphi
1
th.Terminate;
это примитивный метод, который делает всего лишь Th.terminated := true; и по нашему условию цикл закончится


Пример 2
У нас куча потоков и мы хотим остановить их
Кликните здесь для просмотра всего текста
Делаем то же самое но используем для этого общую переменную. Я специально говорю "общую" а не "глобальную".
Хотя одно есть разновидность другого. Я очень не рекомендую использовать глобалки в серьезных проектах
Это не страшно, но очень не здорово. При этом глобалку нужно использовать простого типа - boolean, integer, тогда доступ к ней можно осуществлять без механизмов синхронизации.
Хорошим вариантом будет, например, передать в поток УКАЗАТЕЛЬ на переменную.
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
TMyThread=class
  Terminated2:^boolean;
  ...
end;
 
procedure TMyThread.Execute;
begin
  while not Terminated2^ do
  begin
      .....
  end;
end;
в форме заведем переменную
Delphi
1
2
3
4
5
6
7
8
9
10
11
TForm1=class(TForm)
 ...
  stopThreads:boolean;
end;
...
th := TMyThread.Create(...);
th.Terminated2:=@stopThread;
...
...
 
stopThread := true;
и все потоки остановятся


Пример 3
Если чекбокс установлен, то в потоке делать то, а иначе делать сё
Кликните здесь для просмотра всего текста
Понятно, что никакой чекбокс передавать в поток нельзя. Нам нужен не компонент, а само значение. Вот его мы передадим в поток перед стартом
Delphi
1
2
th:=TMyThread.Create;
th.paramXXX := checkBox1.Checked;
и используем в работе полученное значение
Delphi
1
2
3
4
5
6
7
procedure TMyThread.Execute;
begin
  if paramXXX then
    Proc1()
  else
    Proc2();
end;
мало того. если это значение может меняться в процессе работы потока, вам нужно просто не выбрасывать ссылку на созданный поток ))
Delphi
1
2
3
4
5
6
th := TMyThread.Create(...);
...
procedure TForm1.CheckBox1Checked(Sender:TObject);
begin
  th.paramXXX := checkBox1.Checked;
end;


Пример 4
Любимое. Как из потока менять что-то на форме. Синхронизация.
Кликните здесь для просмотра всего текста

Хорошим способом является передача сообщений через SendMessage();
Но тут начнутся проблемы с правильной работой с памятью, когда передаем строки и много другого, и главное - это не кроссплатформенно! Поэтому я обычно выбираю Synchronize. Еще раз подчеркну. Нельзя вызывать формы и их компоненты и еще много чего без синхронизации, т.е. без указания, что эту процедуру можно выполнять только в главном потоке.
Но это значит, что этот поток остановится и будет ждать, когда процедура выполнится. Поэтому выполняйте синхронизацию как можно реже и как можно быстрее.
Как я уже говорил, мы не даем потоку ссылку на форму. Хотите двигать прогресс-бар, - возьмите ссылку на прогресс-бар и пользуйтесь. Хотите мемо - берите ссылку на мемо. Но помните! Мемо - один из самых тормозных компонентов. 5000 строк - и вся ваша программа 85% времени проводит, добавляя строки в мемо.
Итак, цикл, синхронизация, запись в progressBar. Да еще и не каждый проход цикла, чтобы не молотить зря.
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
th:=TMyThread.Create(...);
th.progressBar := progressBar1;
...
procedure TMyThread.ShowProgress;
begin
  progressBar.position := FIndex div 1000;
end;
 
procedure TMyThread.Execute();
begin
  FIndex:=0;
  while FIndex < 100000 do
  begin
    if index mod 1000 = 0 then
      Synchronize(ShowProgress);
   ....
    inc(FIndex);
  end
end;


upd
тема "Я пишу приложение, которое выкачивает весь интернет по списку из Memo. Скажите как." настолько замусолена уже
что приложу себе ссылку, чтобы потом легче искать
Оптимизация приложения "менеджер закачек"
Метки delphi
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 59
Комментарии
  1. Старый комментарий
    Аватар для Avazart
    согласен. но как раз в случае с прогрессбаром удобно переслать сообщение с числовым параметром, IMHO
    Я так делаю
    Delphi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    
    uses Classes, ComCtrls, Graphics, IdSync;
     
    type
      TProgressNotify= class(TIdNotify)
     
        private
            FProgress:TProgressBar;
            FValue:Int64;
            FStart:Boolean;
     
        protected
           procedure DoNotify;override;
     
        public
             constructor Create(AProgress:TProgressBar;
                              AValue:Int64;
                              AStart:Boolean=False);
      end;
     
    implementation
     
    procedure TProgressNotify.DoNotify;
    begin
        if FStart then
         FProgress.Max:= FValue
      else
         FProgress.Position:= FValue;
    end;
     
    constructor TProgressNotify.Create(AProgress:TProgressBar;
                                       AValue:Int64;
                                       AStart:Boolean=False);
    begin
        FProgress:= AProgress;
        FValue:= AValue;
        FStart:= AStart;
    end;


    И аналогично для строк, для ведения лога в RichEdit c подсветкой цветом и выделением шрифта.
    Запись от Avazart размещена 22.08.2017 в 14:56 Avazart вне форума
  2. Старый комментарий
    Аватар для Massaraksh7
    Статья хорошая, но не для всех классов задач. Для чисто вычислительных задач, когда нужно распределить промежуточные вычисления по процессорам, нужно именно ждать в основной ветке. Можно, конечно, и в блоке OnTerminate отлавливать завершение вычислений, и в нём продолжать дальнейшие вычисления, но при этом программа теряет читаемость и логику.
    Запись от Massaraksh7 размещена 24.08.2017 в 02:10 Massaraksh7 вне форума
  3. Старый комментарий
    Аватар для Avazart
    но при этом программа теряет читаемость и логику.
    Нифига она не теряет, не выдумывайте.
    Именно в OnTerminate и нужно дать завершения это самый очевидный вариант.
    Запись от Avazart размещена 24.08.2017 в 12:08 Avazart вне форума
  4. Старый комментарий
    Аватар для krapotkin
    Цитата Сообщение от Massaraksh7
    нужно именно ждать в основной ветке.
    нутк мы и ждем в основной ветке
    никуда не ходим ))
    читабельность не теряется.
    Delphi
    1
    2
    3
    4
    
    procedure TMainForm.СюдаПриходимКогдаВычисленияВсё();
    begin
       ShowMessage('Всё');
    end;
    куда уж читабельнее
    Запись от krapotkin размещена 24.08.2017 в 13:25 krapotkin вне форума
  5. Старый комментарий
    Аватар для Massaraksh7
    Вы, видимо, не сталкивались с такими задачами. Там, где Ваше Всё, новый блок других вычислений, также требующий распределения по другим процессам, а потом ещё и ещё...
    Запись от Massaraksh7 размещена 24.08.2017 в 14:02 Massaraksh7 вне форума
  6. Старый комментарий
    Аватар для Massaraksh7
    А если эти вычисления ещё оформлены как процедура, или функция, то Ваш вариант вообще не подходит.
    Запись от Massaraksh7 размещена 24.08.2017 в 16:36 Massaraksh7 вне форума
  7. Старый комментарий
    Аватар для Avazart
    Вы, видимо, не сталкивались с такими задачами. Там, где Ваше Всё, новый блок других вычислений, также требующий распределения по другим процессам, а потом ещё и ещё...
    И в чем проблема? Что мешает в OnTerminate породить новый поток или новые потоки ?
    Хотя конечно это может и не требуется, если не нужно завершить самый первый созданный поток вычислений полностью с очисткой памяти.
    Запись от Avazart размещена 24.08.2017 в 18:25 Avazart вне форума
  8. Старый комментарий
    Аватар для krapotkin
    мне уже лень. пока не будет контрпримера, нет смысла
    Запись от krapotkin размещена 24.08.2017 в 18:28 krapotkin вне форума
  9. Старый комментарий
    Аватар для Massaraksh7
    Цитата Сообщение от krapotkin
    мне уже лень. пока не будет контрпримера, нет смысла
    Пожалуйста.
    Функция получает на входе число N. Считает сумму 0...4*N-1, распределяя вычисления по 4 процессам и возвращает результат. Вот так написал её я:
    Delphi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    
    type TMyThread=class(TThread)
       private
         { Private declarations }
       protected
       procedure Execute; override;
       public
       startvalue:uint64;  // начальное значение суммирования
       stopvalue:uint64;   // конечное значение суммирования
       sum:uint64;         // сумма-результат потока
       procedure Add;
       end;
     
    var Th:array [0..3] of TMyThread;
         h,hs:THandle;
         xsum:uint64;
     
    procedure TMyThread.Add;
    begin
    WaitForSingleObject(hs,INFINITE);
    xsum:=xsum+sum;
    ReleaseSemaphore(hs,1,nil);
    end;
     
    procedure TMyThread.Execute;
    var i:uint64;
    begin
    sum:=0; // сумма потока
    i:=startvalue;
    while (i<=stopvalue) do
       begin
       sum:=sum+i;
       i:=i+1;
       end;
    Add;
    ReleaseSemaphore(h,1,nil);
    end;
     
    function LSum(step:uint64):uint64;
    var i,k:integer;
    t:TDateTime;
    l:uint64;
    begin
    for i:=0 to 3 do
       begin
       Th[i]:=TMyThread.Create(True);
       Th[i].FreeOnTerminate:=True;
       l:=step;l:=l*i;
       Th[i].startvalue:=l;        // Распределение начальной и конечной
       Th[i].stopvalue:=l+step-1;  // сумм по потокам
       end;
    xsum:=0;                             // Общая сумма=0
    h:=CreateSemaphore(nil,0,4,nil);    //--Семафор для ожидания завершения потоков
    hs:=CreateSemaphore(nil,1,4,nil);   //--Семафор потоков для доступа к общему ресурсу
    for i:=0 to 3 do Th[i].Start;       // Запуск 4 потоков
     
    for i:=0 to 3 do WaitForSingleObject(h,INFINITE); // Ожидание завершения вычислений
    CloseHandle(h);CloseHandle(hs);
    Result:=xsum;
    end;
    Ваша версия?
    Запись от Massaraksh7 размещена 24.08.2017 в 19:20 Massaraksh7 вне форума
  10. Старый комментарий
    Аватар для Avazart
    Delphi
    1
    
    for i:=0 to 3 do WaitForSingleObject(h,INFINITE); // Ожидание завершения вычислений
    Есть же WaitForMultipleObjects()

    И вообще я не понял зачем здесь семафоры.
    Запись от Avazart размещена 24.08.2017 в 20:53 Avazart вне форума
  11. Старый комментарий
    Потоки не должны ничего читать и писать в формах и компонентах!!!
    А как же BackgroundWorker c ним у меня получилось писать в форме)
    Запись от Алеск размещена 24.08.2017 в 21:54 Алеск вне форума
  12. Старый комментарий
    Аватар для Massaraksh7
    Семафор с хэндлом h для ожидания завершения потоков.
    Семафор с хэндлом hs для доступа к общему участку памяти xsum.
    Но это всё второстепенные вопросы.
    Запись от Massaraksh7 размещена 24.08.2017 в 21:56 Massaraksh7 вне форума
  13. Старый комментарий
    Аватар для Avazart
    Семафор с хэндлом h для ожидания завершения потоков.
    Семафор с хэндлом hs для доступа к общему участку памяти xsum.
    Но это всё второстепенные вопросы.
    Для ожидания завершения потоков можно использовать - хендлы потоков.
    Для синхронизации можно использовать критические секции, или более - OnTerminate.

    Итого - ничто не мешает суммировать результат вычислений каждого потока в OnTerminate без всех этих заморочек.
    Запись от Avazart размещена 24.08.2017 в 23:06 Avazart вне форума
  14. Старый комментарий
    Аватар для Massaraksh7
    Б@#, тут никто ничего не читает. Я даже код привёл, попробуйте переделать под OnTerminate!
    Запись от Massaraksh7 размещена 24.08.2017 в 23:13 Massaraksh7 вне форума
  15. Старый комментарий
    Аватар для Avazart
    Твой код гавеный, нечего ругаться ...
    Пиши нормально и получишь нормальный код в ответ.
    Запись от Avazart размещена 24.08.2017 в 23:19 Avazart вне форума
  16. Старый комментарий
    Аватар для Massaraksh7
    Код тут не при чем. Гавенный или нет - он работает. Я ясно описал задачу, могу повторить:
    Функция получает на входе число N. Считает сумму 0...4*N-1, распределяя вычисления по 4 процессам и возвращает результат.
    Могу я получить нормальный код в ответ?
    Запись от Massaraksh7 размещена 24.08.2017 в 23:26 Massaraksh7 вне форума
  17. Старый комментарий
    Аватар для krapotkin
    в данном коде есть точный и конкретный признак излишнего/неправильного/неоптимального -
    это выполнение синхронизации в конце Execute. Нахрена Зачем делать синхронизацию, если и так вот вот она сама выполнится по факту???

    Ни один семафор в процессе изготовления не пострадал. Вообще ни одного объекта синхронизации не было использовано.
    При этом программа отвечает на все запросы, и даже пишет что-то в мемо.
    Кликните здесь для просмотра всего текста
    Delphi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    
    unit UMainForm;
     
    interface
     
    uses
      classes, types, forms, Controls, StdCtrls, Dialogs, SysUtils, ExtCtrls;
     
    type
     
      TMainForm = class(TForm)
        bStart: TButton;
        m1: TMemo;
        tmr1: TTimer;
        procedure bStartClick(Sender: TObject);
        procedure tmr1Timer(Sender: TObject);
      private
        FSUM : Int64;
        FFinished:integer;
        procedure Log(const s: string);
     
        procedure ThreadTerm(Sender: TObject);  public
        procedure RunThread(AID:integer);
      end;
     
    var
      MainForm: TMainForm;
    const
      INTERVAL=65536;
      THREAD_COUNT=4;
     
    implementation
    uses UMyThread;
     
    {$R *.dfm}
     
    { TMainForm }
     
    procedure TMainForm.Log(const s:string);
    begin
      m1.Lines.add(TimeToStr(Now)+' '+s );
    end;
     
    procedure TMainForm.ThreadTerm(Sender: TObject);
    var TH:TMyThread absolute Sender;
    begin
      log(Format('Поток %d завершен с промежуточным результатом %d',[th.FID,th.Result]));
      FSUM := FSUM + th.Result;
      inc(FFinished);
      if FFinished=THREAD_COUNT then
      begin
        log('==========================');
        log(Format('Работа окончена. Итог: %d',[FSum]));
        Log('');
        Log('');
        bStart.Enabled := true;
        tmr1.Enabled := false;
      end;
    end;
     
    procedure TMainForm.bStartClick(Sender: TObject);
    var
      I: Integer;
    begin
      bStart.Enabled := false;
      FSUM := 0;
      FFinished := 0;
      tmr1.Tag := 0;
      tmr1.Enabled := True;
      Log('Начало работы');
      for I := 0 to THREAD_COUNT-1 do
        RunThread(i);
    end;
     
    procedure TMainForm.RunThread(AID:integer);
    var
      th:TMyThread;
    begin
      th:=TMyThread.Create(AID, INTERVAL div THREAD_COUNT * AID, INTERVAL div THREAD_COUNT * (AID+1)-1, ThreadTerm);
      th.Resume;
    end;
     
    procedure TMainForm.tmr1Timer(Sender: TObject);
    begin
      Log(Format('Прошло %d сек',[tmr1.Tag]));
      tmr1.Tag := tmr1.Tag + 1;
    end;
     
    end.


    Кликните здесь для просмотра всего текста
    Delphi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    unit UMyThread;
     
    interface
    uses Classes;
    type
      TMyThread = class(TThread)
        FID:integer;
        FFirst, FLast:int64;
        Result : int64;
        constructor Create(AID:integer; AFirst,ALast:int64; ATermProc:TNotifyEvent);
        procedure Execute; override;
      end;
     
    implementation
     
    { TMyThread }
     
    constructor TMyThread.Create(AID:Integer; AFirst,ALast:int64; ATermProc:TNotifyEvent);
    begin
      inherited Create(True);
      FID := AID;
      FFirst := AFirst;
      FLast := ALast;
      FreeOnTerminate := true;
      OnTerminate := ATermProc;
    end;
     
    procedure TMyThread.Execute;
    var
      i:int64;
    begin
      result := 0;
      i:=FFirst;
      while i<=FLast do
      begin
        result := result + i;
        Sleep(Random(3));
        Inc(i);
      end;
    end;
     
    end.
    Запись от krapotkin размещена 25.08.2017 в 07:32 krapotkin вне форума
  18. Старый комментарий
    Аватар для Massaraksh7
    Иными словами, поставленную задачу Вы решить не можете?
    Запись от Massaraksh7 размещена 25.08.2017 в 12:03 Massaraksh7 вне форума
  19. Старый комментарий
    Аватар для Avazart
    Как я сказал научитесь нормально писать код.
    Всем срать на то что ваш код "работает", если его нельзя легко читать.

    "Иными словами" я ответил как можно решить задачу без всего хлама.
    Запись от Avazart размещена 25.08.2017 в 12:05 Avazart вне форума
  20. Старый комментарий
    Аватар для Massaraksh7
    Защитано.
    Запись от Massaraksh7 размещена 25.08.2017 в 12:09 Massaraksh7 вне форума
 
Новые блоги и статьи
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
Программа опроса у.з. расходомера SLS-720F
Argus19 02.09.2026
Программа опроса у. з. расходомера SLS-720F Программа опрашивает один раз в минуту три ультразвуковых расходомера SLS-720F через интерфейс RS-485 по протоколу Modbus RTU. Опрашиваются регистры. . .
Hyper-V: Компьютер должен поддерживать доверенный платформенный модуль 2.0.
Maks 31.08.2026
При установке Windows 11 на виртуальную машину Hyper-V 2-го поколения вылезла такая ошибка: Решение: в параметрах виртуальной машины, в разделе "Безопасность" (Security) активировать флаг. . .
Архитектура биовида Стива в Майнкрафте: Зачем бонобо кубический каннибализм
anaschu 30.08.2026
Кубический Вагинокапитализм в Minecraft: Математический инвариант ОДУ и рок Стивов-бонобо Главная задача разработанной «Модели Всего» — наглядно продемонстрировать наличие системной «судьбы». . .
Оттачиваю умение писать js программы.
russiannick 30.08.2026
Проектом выходного дня стало написание Книги шифров Виженера. Итогом стала версия 200, синий туман. Синий туман назван так, потому что замораживает текст под собой. Нажатие синих кнопок управляют. . .
мат медиц модель 30. презентация проекта
anaschu 27.08.2026
хоп хоп хоп хидахоп, а я кладую))
Как у меня протекала болезнь
zorxor 27.08.2026
Здравствуйте, друзья! Эта запись блога предназначена именно для вас - для моих дорогих друзей, которые знали меня лично. Чтобы ответить на вопрос - а что же со мной произошло на самом деле? Я учился. . .
Нашел вот забавное видео о измерениях. Лучшее что я видел на эту тему
kumehtar 26.08.2026
ILETXiw9bMQ Основная суть и тезисы по измерениям: 0D (Нулевое измерение): точка, не имеющая длины, ширины, высоты или объема. Объект не может перемещаться в 0D. 1D (Первое измерение):. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru