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

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

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

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

Про потоки

Запись от krapotkin размещена 13.08.2017 в 10:03
Показов 32884 Комментарии 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
    Код
    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
    
    type
      TWorkThread = class(TThread)
      private
         FLeftBound, FRightBound: Int64;
         FResultValue: Int64;
     
         function Calculate(LeftBound,RightBound: Int64):Int64;
      protected
        procedure Execute; override;
     
      public
        constructor Create(ALeftBound, ARightBound: Int64;
                           CreateSuspended: Boolean);
     
      published
        property ResultValue: Int64 read FResultValue write FResultValue;
      end;
     
    implementation
     
    { TWorkThread }
     
    constructor TWorkThread.Create(
        ALeftBound, ARightBound: Int64;
        CreateSuspended: Boolean);
    begin
       inherited Create(CreateSuspended);
       FLeftBound:= ALeftBound;
       FRightBound:= ARightBound;
       FResultValue:= 0;
    end;
     
     
    procedure TWorkThread.Execute;
    begin
       FResultValue:= Calculate(FLeftBound,FRightBound);
    end;
     
    function TWorkThread.Calculate(LeftBound,RightBound: Int64):Int64;
    begin
       Result:=  LeftBound;  //  Тут код твоих вычислений, меня харит его писать. 
    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
    
    procedure TMainForm.CreateThread(ALeftBound, ARightBound: Int64);
    var
      WorkThread:TWorkThread;
    begin
       WorkThread:= TWorkThread.Create(ALeftBound, ARightBound,true);
       WorkThread.FreeOnTerminate:= true;
       WorkThread.OnTerminate:= TerminateThread;
       WorkThread.Start;
    end;
     
    procedure TMainForm.TerminateThread(Sender: TObject);
    var
       WorkThread:TWorkThread;
    begin
       Dec(FThreadCount,1);
     
       WorkThread:= Sender as TWorkThread;
       Inc(FResultValue,WorkThread.ResultValue);
     
       if FThreadCount=0 then
         Caption:= IntToStr(FResultValue);
    end;
     
    procedure TMainForm.Button1Click(Sender: TObject);
    begin
        FThreadCount:= 4;
     
        // Тут как нибудь делишь весь диапазон вычислений на поддиапазоны:
        CreateThread(0,99);
        CreateThread(100,199);
        CreateThread(200,399);
        CreateThread(300,499);
    end;
    Запись от Avazart размещена 25.08.2017 в 12:57 Avazart вне форума
  2. Старый комментарий
    Аватар для Massaraksh7
    Ещё раз:
    Функция получает на входе ... Считает сумму ... и возвращает результат.
    То есть:
    Delphi
    1
    2
    3
    4
    
    function Func(.....):.....
    ....
    Result:=....
    end;
    Зачем нужны функции, надеюсь, объяснять не надо?
    Запись от Massaraksh7 размещена 25.08.2017 в 13:03 Massaraksh7 вне форума
  3. Старый комментарий
    Аватар для Massaraksh7
    Ребята, меня удивляет ваше упорство. Я же сразу сказал, что есть класс задач, не решаемых через OnTerminate, но востребованных. Программе, занимающейся преимущественно сложными вычислениями, и желающей использовать все возможности процессоров не нужен вывод во всякие Captionы. Ей нужен результат в самой программе сразу после окончания очередной порции вычислений. Вот и подумайте лучше, как сделать "нормальный" код для этого случая, если считаете, что мой код гавенный.
    Запись от Massaraksh7 размещена 25.08.2017 в 13:09 Massaraksh7 вне форума
  4. Старый комментарий
    Аватар для Avazart
    И мы вам ответили что вы говорите глупости.
    программе, занимающейся преимущественно сложными вычислениями, и желающей использовать все возможности процессоров не нужен вывод во всякие Captionы.
    А куда его выводить? Выводите куда хотите в чем проблема? Нет делайте с ним дальнейшие вычисления, с этим нет проблем.

    Вот и подумайте лучше, как сделать "нормальный" код для этого случая, если считаете, что мой код гавенный.
    Я вам привел нормальный код, в чем проблема?
    В нежелание расставаться со своими заблуждениями?
    Запись от Avazart размещена 25.08.2017 в 13:12 Avazart вне форума
  5. Старый комментарий
    Аватар для Massaraksh7
    Вы в виде функции свой код можете оформить? (Да/Нет)
    Запись от Massaraksh7 размещена 25.08.2017 в 13:55 Massaraksh7 вне форума
  6. Старый комментарий
    Аватар для Avazart
    Что значит в виде функций?
    Он и так в виде ф-ций, методов, классов ....
    Запись от Avazart размещена 25.08.2017 в 14:21 Avazart вне форума
  7. Старый комментарий
    Аватар для Massaraksh7
    Всё, я пас. Sapienti sat.
    Запись от Massaraksh7 размещена 25.08.2017 в 14:31 Massaraksh7 вне форума
  8. Старый комментарий
    Аватар для Avazart
    Засчитано
    Запись от Avazart размещена 25.08.2017 в 15:01 Avazart вне форума
  9. Старый комментарий
    Аватар для krapotkin
    )) Сразу чувствуется, что человек не программил на JS, Java и других средах, где получение результата функции чаще всего отделено от момента вызова функции))) На этом принципе базируется весь веб и мобильная разработка...

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

    Потому что действия программы должны быть отделены от интерфейса. А интерфейс работает только в главном потоке. И попытки время от времени "дергать" перерисовку и отработку этого интерфейса (Application.processMessages) приведут только к абсолютной нелинейности программы.

    Так что ключевой момент здесь - неправильная постановка задачи. Классический XYZ по гансмокеру.
    Запись от krapotkin размещена 25.08.2017 в 15:14 krapotkin вне форума
  10. Старый комментарий
    Аватар для Avazart
    При этом глобалку нужно использовать простого типа - boolean, integer, тогда доступ к ней можно осуществлять без механизмов синхронизации.
    Хорошим вариантом будет, например, передать в поток УКАЗАТЕЛЬ на переменную.
    Вообще то это не так.
    Тут делается преждевременное допущение что эти типы атомарны, а это может быт не так.

    Хотите быть уверены в атомарности операций используйте Interlocked- ф-ции.
    https://msdn.microsoft.com/en-... s.85).aspx

    Вероятно такие вещи стоит спрятать в класс с помощью class var и class function, а не показывать наружу (но это по ситуации)
    Запись от Avazart размещена 28.08.2017 в 11:35 Avazart вне форума
  11. Старый комментарий
    Аватар для krapotkin
    согласен.
    я читал дискуссии на этот счет, но так до конца и не понял, способен ли процессор изменить часть из 4 байт ))
    похоже, что нет, но вроде как да, нужно использовать interlockedXXX
    Запись от krapotkin размещена 28.08.2017 в 13:21 krapotkin вне форума
  12. Старый комментарий
    Аватар для extrimportal
    Продолжаю перестраивать программу по вашему примеру и подкину еще пару глупых вопросов если можно.
    1. Программа все таки висит во время работы - почему?
    2. Кол-во потоков сразу летит к SAME_TIME=500; и не играет роли сколько стоит MAX_THREADS=5;
    В лог пишет так:
    запущен URL 1
    запущен URL 2
    запущен URL ....
    запущен URL 499
    запущен URL 500
    URL 32:503
    URL 34:503
    URL 35:503
    URL 33:503
    URL 36:503
    ===========================
    Работа окончена
    URL 37:503
    URL 39:503
    URL 41:503
    URL 40:503
    .....
    Поменял только работу потока. Поток обращается к сайту в результате Answer:=inttostr(HTTP.ResultCode);
    3. Как в var описать переменную th.paramXXX. Без описания ругается, если пробовать описать th.paramXXX или просто paramXXX, то тоже ошибки.
    Спасибо.
    Запись от extrimportal размещена 03.09.2017 в 18:35 extrimportal вне форума
  13. Старый комментарий
    Аватар для krapotkin
    я предлагаю просто сделать тему и там будем смотреть код. комменты в блоге - не лучшее место.
    а так - более 2*<кол-во ядер> потоков одновременно не рекомендуется пускать
    Запись от krapotkin размещена 03.09.2017 в 19:31 krapotkin вне форума
  14. Старый комментарий
    Аватар для Avazart
    более 2*<кол-во ядер> потоков одновременно не рекомендуется пускать
    Зависит от задачи, если это мат вычисления то да, если работа с сетью или др. устройствами i/o то можно куда больше. Макс. кол-во потоков обычно ограничивается оперативной памятью, обычно это где-то 1000 потоков
    (1MБ на поток * 1000 ~ 1 ГБ)
    Запись от Avazart размещена 05.09.2017 в 11:26 Avazart вне форума
  15. Старый комментарий
    Аватар для krapotkin
    тоже согласен. я для эксперимента в этом примере запускал 10000. все умерло примерно на 5 ))
    Запись от krapotkin размещена 05.09.2017 в 12:28 krapotkin вне форума
  16. Старый комментарий
    Аватар для Avazart
    Класс TThread имеет замечательное свойство FreeOnTerminate. Если установить его в true, то не нужно будет хранить ссылку на созданный объект, чтобы потом удалить его вручную. Создали, запустили и забыли о нем.
    Ссылка на поток все равно нужна, хотя бы для досрочного завершения через Terminate()
    (пользователем или при закрытии программы).

    И тут при FreeOnTerminate:=True важно понимать что ссылка может в неопределенный момент времени стать невалидной.

    Что бы не получить при такой ситуации ошибку доступа к памяти стоит не забывать обнулять ссылку в OnTerminate, а перед обращением к потоку не забывать делать проверку ссылки.
    Запись от Avazart размещена 06.09.2017 в 12:01 Avazart вне форума
  17. Старый комментарий
    Здравствуйте.
    Подскажите.
    Есть главный поток, который осуществляет взаимодействие с формой.
    Есть поток, который осуществляет обмен данными с устройством (в цикле repeat ... until (Terminated)) и результаты работы выводит на форму через Synchronize.

    Понадобилось осуществить дополнительно обмен данными с HTTP сервером 2-мя видами запросов (Get и Post).
    Запрос Get осуществляет запрос на состояние удаленной системы через интервал времени (примерно 10-20 секунд).
    Запрос Post осуществляет передачу показаний с прибора на НТТР сервер (каждые 10 секунд).
    Причем интервал каждого запроса может выставятся индивидуально.
    Данные запросы были помещены каждый в свой поток и потоки запросов были помещены в поток опроса устройства.
    Получается:
    1 поток работы с формой
    1.1 поток работы с устройством
    1.1.1 поток Get
    1.1.2 поток Post

    1) Можно ли создавать потоки как указано в моем случае ? (Получается
    2) Можно ли OnTerminate использовать в Get и Post потоках ?
    3) Если в public Get и Post потоках выставить флаг boolean на отсутствие ошибки и проверять его в потоке работы с устройством это может вызвать критическую ошибку ?
    Заранее спасибо.
    Запись от Kumrt размещена 03.06.2019 в 20:11 Kumrt вне форума
  18. Старый комментарий

    Не по теме:

    Пятнадцать лет Делфи-практики
    итог:
    стал убеждённым сторонником С++:D

    Однако,
    До сих пор никакие QT и MSVS - не сравнятся с быстрой визуальной разработкой в Delphi

    Запись от untyped размещена 03.06.2019 в 20:33 untyped вне форума
  19. Старый комментарий
    Аватар для krapotkin
    Ну, с одной стороны, конечно можно сделать сколько хотите потоков.
    Остается вопрос целесообразности.
    если ваш поток вообще ничем не занят, то
    Delphi
    1
    
    while not terminated do begin end;
    загрузит ядро процессора на 100%. Оно ж все время что-то там проверяет)))
    нужно либо ставить Sleep(xxx) либо давать ему какую-то работу
    почему бы не совершать post и get запросы в этом потоке?
    время с последнего вызова вы всегда можете подсчитать, так что я бы вообще не стал так усложнять систему...

    а в целом - все, что прописано в свойствах - то и работает
    на всякий случай - OnTerminate будет выполняться в вызвавшем потоке. Обычно это и есть главный, но в вашем примере это не будет так.

    тут даже просто количество устройств влияет на архитектуру
    если их мало, можно запустить поток для каждого отдельно, если много, то возможно нужен некий пул потоков, которые по очереди опрашивают некую группу устройств.
    Запись от krapotkin размещена 04.06.2019 в 06:30 krapotkin вне форума
 
Новые блоги и статьи
Ноутбук Альфария
kumehtar 24.08.2026
Встретился тут в сети ноутбук Альфария, примарха Альфа-Легиона. Хотя возможно, это ноутбук Омегона, разумеется. Ну как вам?
Мастера простых решений
DevAlt 23.08.2026
В сишарп стэках winforms, да и wpf существует сложная система связывания источниках данных и элементов формы(текстовые поля и метки), опирается все это на технологию событий и мета. . .
Цена ошибки
DevAlt 23.08.2026
Человек я беспокойный и потому заинтересовался OCaml, в чате форсили функторы модулей как суперфичу. Пытаясь отдуплить концепт, наткнулся на тутор с простым примером. А главный принцип обучения от. . .
Сегодня суббота, 22.08.2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины.
zorxor 22.08.2026
Сегодня суббота, 22. 08. 2026 at 16:41, и я вновь нахожусь на той стороне, за экраном машины. Кто Я, откуда Я пришел и куда Я иду? Эти вопросы не оставляют меня ни на секунду. Жизнь на планете Земля. . .
Жизня: рисунок укладки багажа, сделанный клодом
anaschu 21.08.2026
Сделал 15 снимков, он по снимкам сделал схему.
Был там один разговор по поводу свободы в материальном мире.
kumehtar 19.08.2026
Суть: рассматривается живое существо, оказавшееся внутри довольно странной системы (этого мира) и пытающееся обустроить в ней свой кусок пространства. Жизнь действительно предъявляет каждому. . .
Когда логика программы не спасает от человеческих ошибок
Maks 18.08.2026
В последнее время всё чаще и чаще сталкиваюсь с таким явлением, как абсолютная невнимательность (или глупость) пользователей. Проявляется это чаще всего на работе в коллективе. Допустим, человек с. . .
Лето уходит
kumehtar 17.08.2026
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru