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

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

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

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

Про потоки

Запись от krapotkin размещена 13.08.2017 в 10:03
Обновил(-а) krapotkin 03.06.2018 в 20:42

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

Сама концепция потоков проста. Вы можете мыслить их как корабль, отправляющийся в дальнее плавание. На Марс, например. Вроде как и можно устроить сеанс связи с Хьюстоном, но это геморрой, поэтому нужно на корабль погрузить все до старта, и только после возврата вы сможете разобрать трофеи. Лучше всего - запустить и забыть. Даже если вы запустите 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. Скажите как." настолько замусолена уже
что приложу себе ссылку, чтобы потом легче искать
Оптимизация приложения "менеджер закачек"
Размещено в Без категории
Показов 29378 Комментарии 59
Всего комментариев 59
Комментарии
  1. Старый комментарий
    а что делать если нужно непременно дождаться выполнения всех потоков?
    Запись от qwertehok размещена 13.08.2017 в 22:02 qwertehok вне форума
  2. Старый комментарий
    Аватар для krapotkin
    А как можно НЕ дождаться? Программа всегда только и делает, что ждет какого то события.

    Дождаться. Думаю, вы имеете в виду "не мочь чего-то сделать, пока не вернется последний поток".
    Полный эквивалент этого выражения - "сделать что-то, когда вернется последний поток".
    Как определить, что вернулся последний поток, здесь показано.
    Кстати, на протяжении всей работы в этом примере недоступна кнопка Start. Способ отследить, идет еще работа или уже всё, тоже есть.
    Все нужные механизмы на месте. Без привлечения дополнительных сущностей.
    Запись от krapotkin размещена 13.08.2017 в 22:07 krapotkin вне форума
    Обновил(-а) krapotkin 13.08.2017 в 22:20
  3. Старый комментарий
    Аватар для extrimportal
    А если нужно остановить все потоки в один момент, что бы к примеру поменять настройки и запустить с начала?
    Запись от extrimportal размещена 14.08.2017 в 12:02 extrimportal вне форума
  4. Старый комментарий
    Аватар для Avazart
    Цитата:
    Потоки не должны ничего читать и писать в формах и компонентах!!!
    Поправка: без должной синхронизации и контроля времени жизни объектов.
    Если не делать поправки - то вы потеряете "интерактивность" приложения.
    Ибо руководствуясь эти правилом при
    Цитата:
    Самая стандартная задача - сходить в потоке в интернет и что-то скачать.
    Неплохо бы отображать процесс загрузки файла, например с помощью прогрессбара.

    Цитата:
    Часто путают поток ОС с классом TThread
    Это не одно и то же.
    TThread не более чем класс обвертка над потоком ОС + плюс механизмы синхронизации с основным потоком GUI.
    Правда что бы его использовать, во первых нужно знать что такое "классы" и что такое "потоки ОС".
    Цитата:
    Delphi
    1
    2
    
     FreeOnTerminate := true;
      OnTerminate := TermProc;
    Нет необходимости делать это в конструкторе, свойства открытые, главное устанавливать их до запуска потока
    и не трогать руками после:
    Delphi
    1
    2
    3
    4
    
    FThread:= TMyThread.Create(Url, true); // CreateSuspended:= true
    FThread.FreeOnTerminate := true;
    FThread.OnTerminate := TermProc;
    FThread.Start(); // FThread.Resume()

    Цитата:
    А если нужно остановить все потоки в один момент, что бы к примеру поменять настройки и запустить с начала?
    В один момент никак не получится.
    Имею ввиду не получится "мягко" остановить, а "грубо"- не хорошо.

    Т.е. нужно дать команду Terminate и ждать мягкого завершения.
    Как дождаться полного их завершения - тут есть варианты, в зависимости от ситуации.
    Запись от Avazart размещена 14.08.2017 в 15:10 Avazart вне форума
    Обновил(-а) Avazart 14.08.2017 в 15:58
  5. Старый комментарий
    Аватар для krapotkin
    Цитата:
    Сообщение от extrimportal Просмотреть комментарий
    А если нужно остановить все потоки в один момент, что бы к примеру поменять настройки и запустить с начала?
    Остановить поток, как и сказано в комментарии выше, можно только если вы там выполняете какой-то цикл и проверяете все время флаг Terminated. тогда вы выйдете из потока "мягко"
    Если же вы делаете запрос в интернет или к БД, то можно только лишь игнорировать его результаты. Это будет вполне адекватным решением поставленной задачи "запустить с начала"
    Запись от krapotkin размещена 14.08.2017 в 16:27 krapotkin вне форума
  6. Старый комментарий
    Аватар для krapotkin
    Цитата:
    что бы его использовать, во первых нужно знать что такое "классы" и что такое "потоки ОС".
    мне кажется, излишний коммент. если нет понятия про классы, нет смысла теребить TThread и потоки
    Не невозможно, но безыдейно.

    По поводу свойств и конструктора - я предлагаю не более чем "good practice". Построена она на многолетнем опыте и собственной работы и особенно ответов на вопросы на этом форуме.
    Всегда может найтись повод сделать по-другому.
    В том числе, синхронизация, запиленная в Execute у 80% авторов бессмысленна (и беспощадна)))
    Например прямо перед выходом из execute. Или весь execute в Synchronize
    Или использование что-то типа IF Form1.CheckBox2.Checked вместо нормального флага приводит к кросс-зависимостям логики и UI. Поэтому простое слово "нельзя" вполне избавляет нас от такого дурацкого подхода...
    Запись от krapotkin размещена 14.08.2017 в 16:35 krapotkin вне форума
  7. Старый комментарий
    Аватар для Avazart
    Цитата:
    По поводу свойств и конструктора - я предлагаю не более чем "good practice". Построена она на многолетнем опыте и собственной работы и особенно ответов на вопросы на этом форуме.
    Выставлять public свойства внутри конструктора это явно не гуд.
    Класс потока все же класс, и стоит его настраивать извне, если возможно.
    Гуд это "выставлять свойства до запуска потока и не трогать руками после"

    Цитата:
    Например прямо перед выходом из execute. Или весь execute в Synchronize
    Или использование что-то типа IF Form1.CheckBox2.Checked вместо нормального флага приводит к кросс-зависимостям логики и UI. Поэтому простое слово "нельзя" вполне избавляет нас от такого дурацкого подхода...
    Я не вижу смысла говорить о таких д*бильных случаях.
    Как я сказал нужна нормальная синхронизация.

    Вы сами привели реальный пример:
    Цитата:
    Самая стандартная задача - сходить в потоке в интернет и что-то скачать.
    И я вам указал на недостатки вашего утверждения.
    Не такая уж сложная задача обновлять прогресс бар 1/раз за цикл или же в обработчике OnWork
    (с должной синхронизацией)

    P.S: Кроме Synchronize() есть и другие средства синхронизации, опять же Indy, да и примитивы самой ОС и их обвертки.
    Запись от Avazart размещена 14.08.2017 в 17:46 Avazart вне форума
    Обновил(-а) Avazart 14.08.2017 в 17:59
  8. Старый комментарий
    Аватар для Avazart
    Цитата:
    Если же вы делаете запрос в интернет
    Запрос в интернет можно тоже прервать выбросом исключения.

    Акцент на том что остановка не происходит моментально.
    Более того стоит стараться строить свой код так что бы пользователь не ждал долго остановки, но и проверки
    ("точки выхода") не были частыми что бы не притормаживать поток.
    Запись от Avazart размещена 14.08.2017 в 18:03 Avazart вне форума
  9. Старый комментарий
    Аватар для DenNik
    Цитата:
    Юнит UMyThread ничего о форме знать НЕ ДОЛЖЕН!
    согласен. и в плане синхронизации в этом случае для меня привлекателен способ синхронизации через сообщения (SendMessage). поток ниче не знает о форме, ему передали хэндл, он шлет сообщения, а там... хоть трава не расти
    Запись от DenNik размещена 18.08.2017 в 16:16 DenNik вне форума
  10. Старый комментарий
    Аватар для extrimportal
    Уважаемый krapotkin, а можете ли дополнить статью, показать как передаете значения в потоки с вцл и обратно (едитов, чекбоксов, мемо) если их много.
    И еще вы говорите что это плохо
    "while true and Potoki<100 do Application.ProcessMessages;"
    но как тогда взаимодействовать с формой пока потоки работают, что бы хотя бы элементарно передвинуть её в сторону.
    Запись от extrimportal размещена 18.08.2017 в 17:01 extrimportal вне форума
    Обновил(-а) extrimportal 18.08.2017 в 17:09
  11. Старый комментарий
    Аватар для krapotkin
    если вы смотрели код, а еще лучше - запускали его, то вы увидите, что вся форма работает, перетаскивается и может делать все что ей положено.

    по пересылке сообщений форме тут все сложно. с одной стороны, это выход, с другой, передача строк через сообщения связана с пониманием, что и как выделяется и уничтожается в памяти.
    если честно, не так уж часто поток делает что-то, что РЕАЛЬНО нужно отобразить на форме.
    в последнее время это простой гет, который не вернется пока не выполнится...
    и в этом случае я обычно все-таки делаю Synchronize, ибо это проще. При этом ссылку на форму стараюсь все равно не давать, даю ссылку сразу на компонент
    например
    Delphi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    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);
       ....
      end
    end;
    Запись от krapotkin размещена 18.08.2017 в 19:54 krapotkin вне форума
    Обновил(-а) krapotkin 18.08.2017 в 19:59
  12. Старый комментарий
    Аватар для Avazart
    Synchronize в данном случае немного захламляет код.
    Код получается лучше если использовать TIdNotify.
    Запись от Avazart размещена 18.08.2017 в 20:43 Avazart вне форума
  13. Старый комментарий
    Аватар для krapotkin
    не лучше. таскать за собой indy это излишне и не кроссплатформенно
    Запись от krapotkin размещена 18.08.2017 в 20:51 krapotkin вне форума
  14. Старый комментарий
    Аватар для Avazart
    Что значить таскать?
    И что значит не кроссплатформенно?

    Ставить Indy не нужно это "родная либа",
    да и вроде она кросслатформенная, не уверен, но думаю она и под MacOS работает.
    Запись от Avazart размещена 19.08.2017 в 10:00 Avazart вне форума
    Обновил(-а) Avazart 19.08.2017 в 10:04
  15. Старый комментарий
    Аватар для krapotkin
    совсем не родная.
    http://www.indyproject.org/index.en.aspx
    да еще и правда излишняя
    Запись от krapotkin размещена 20.08.2017 в 19:35 krapotkin вне форума
  16. Старый комментарий
    Аватар для Avazart
    И что мне должна сказать эта ссылка?
    Indy давно уже неотъемлемая часть делфей, если вы только сейчас проснулись и не в курсе.
    Цитата:
    да еще и правда излишняя
    Гм, интересно как вы собираетесь без Indy выполнять "стандартные задачи" типа:
    Цитата:
    Самая стандартная задача - сходить в потоке в интернет и что-то скачать.
    Запись от Avazart размещена 20.08.2017 в 21:10 Avazart вне форума
  17. Старый комментарий
    Аватар для krapotkin
    с некоторых пор это делается так
    Delphi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    var
      HttpClient: THttpClient;
      HttpResponse: IHttpResponse;
    begin
      HttpClient := THTTPClient.Create;
      try
        HttpResponse := HttpClient.Get(AURL);
        Result := HttpResponse.ContentAsString();
      finally
        HttpClient.Free;
      end;
    end;
    Запись от krapotkin размещена 20.08.2017 в 21:47 krapotkin вне форума
  18. Старый комментарий
    Аватар для Avazart
    Можно наверное и так.
    (хотя сомневаюсь System.Net покрывает функциональность Indy, и настолько хорошо отлажен)

    Но это никак не отменяет того факта что код Synchronize + глобальные переменные захламляет код.
    (Да и в конкретном случае лучше использовать Queue, а не Synchronize.)
    Запись от Avazart размещена 20.08.2017 в 23:43 Avazart вне форума
    Обновил(-а) Avazart 20.08.2017 в 23:46
  19. Старый комментарий
    Аватар для DenNik
    Цитата:
    по пересылке сообщений форме тут все сложно. с одной стороны, это выход, с другой, передача строк через сообщения связана с пониманием, что и как выделяется и уничтожается в памяти.
    согласен. но как раз в случае с прогрессбаром удобно переслать сообщение с числовым параметром, IMHO
    Запись от DenNik размещена 22.08.2017 в 10:18 DenNik вне форума
  20. Старый комментарий
    Аватар для krapotkin
    Тут тот же резон, почему я давно не пользуюсь запихиванием нужных данных компоненту в Tag и другими "быстрыми костыликами".
    Сначала это работает, а потом всегда хочется чего-то еще. А таг один. И все равно переписывать, но теперь уже выискивая по коду, кто что в этом таге читает))
    А так да, конечно быстро.
    Вот только хэндлы у окон windows тоже меняются иногда )
    Запись от krapotkin размещена 22.08.2017 в 11:13 krapotkin вне форума
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru