Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 24.08.2016, 16:39   #1
tarakan1983
Форумчанин
 
Аватар для tarakan1983
 
Регистрация: 09.09.2008
Сообщений: 418
По умолчанию Синхронизация потоков

Подскажите, пожалуйста, на моем примере как правильно синхронизировать несколько потоков.
На форме есть n количество combobox, каждый заполняется из разных таблиц БД.
Я делал так
1. Выключаю первый combobox. ( synchronize(comboboxN.enable = false) )
2. В Execute создаю TstringList и заполняю его данными из таблицы N, т.е. while not tableN.eof do TstringListN.add(tableN.poleN)
3. Потом synchronize (comboboxN.items := TstringListN)
4. И включаю ComboboxN ( synchronize(comboboxN.enable = false) )
Так я проделываю со всеми Combobox по очереди, а хочется запустить несколько потоков, заполнить все Combobox и по окончании включить сразу все. Т.е. я понимаю так, что должен быть поток, который бы запускал все остальные и проверял : все законченно или нет. Если все потоки отработали, то включил все combobox’ы. Подскажите как это осуществить?
tarakan1983 вне форума Ответить с цитированием
Старый 24.08.2016, 16:52   #2
Аватар
Старожил
 
Аватар для Аватар
 
Регистрация: 17.11.2010
Сообщений: 18,922
По умолчанию

Смысл этого в чем?
Если бы архитекторы строили здания так, как программисты пишут программы, то первый залетевший дятел разрушил бы цивилизацию
Аватар вне форума Ответить с цитированием
Старый 24.08.2016, 17:01   #3
tarakan1983
Форумчанин
 
Аватар для tarakan1983
 
Регистрация: 09.09.2008
Сообщений: 418
По умолчанию

Цитата:
Сообщение от Аватар Посмотреть сообщение
Смысл этого в чем?
Ну первый смысл научиться работать с потоками и их синхронизация. Прочитано много, а осознано 5% от прочитанного.
tarakan1983 вне форума Ответить с цитированием
Старый 24.08.2016, 17:46   #4
eoln
Старожил
 
Аватар для eoln
 
Регистрация: 26.04.2008
Сообщений: 2,645
По умолчанию

Один из примеров для WaitFor***Objects (без обработки ошибок и зависаний потоков, а также без чистки за собой мусора)
Код:
type
  MainPotok = class(TThread) //управляющий поток
    procedure Execute; override;
  end;

  PotokForBD = class(TThread) //потоки которые работают с БД
    procedure Execute; override;
  end;

procedure MainPotok.Execute;
var
  i: integer;
  myThreadHandle: array[1..5] of THandle;
begin
  //сначала выключаем все enabled через synchronize
  ...
  for i := 1 to 5 do //запускаем потоки для обработки БД(например, 5 штук) и сохраняем хэндлы
    myThreadHandle[i] := (PotokForBD.Create).Handle;

  //далее бесконечно ждём завершения всех потоков
  WaitForMultipleObjects(5,@myThreadHandle,true,INFINITE);
  ...
  //и в конце включаем все enabled через synchronize
end;

procedure PotokForBD.Execute;
begin
  //поток что-то делает с БД
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  MainPotok.Create;//запуск управляющего потока
end;
Литература: https://msdn.microsoft.com/ru-ru/lib...(v=vs.85).aspx
https://msdn.microsoft.com/ru-ru/lib...(v=vs.85).aspx

Также полезно ознакомится с семафорами
eoln вне форума Ответить с цитированием
Старый 24.08.2016, 17:48   #5
tarakan1983
Форумчанин
 
Аватар для tarakan1983
 
Регистрация: 09.09.2008
Сообщений: 418
По умолчанию

Цитата:
Сообщение от eoln Посмотреть сообщение
Один из примеров для WaitFor***Objects (без обработки ошибок и зависаний потоков, а также без чистки за собой мусора)
Также полезно ознакомится с семафорами
Спасибо, теперь есть более четкое направление для самообразования

Последний раз редактировалось tarakan1983; 24.08.2016 в 17:53.
tarakan1983 вне форума Ответить с цитированием
Старый 24.08.2016, 20:08   #6
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

tarakan1983
Вы выбрали очень плохую задачу для обучения.
1) Быстрее не будет. Если раньше галочки загорались постепенно. То теперь программа будет висеть дольше и всё будет загораться одновременно.
2) Не все БД поддерживают парольную работу. А те которые поддерживают возвращают разные версии таблиц.

Цитата:
Ну первый смысл научиться работать с потоками и их синхронизация. Прочитано много, а осознано 5% от прочитанного.
И не говори. Все книжки которые читал смело можно выкинуть на помойку.

Цитата:
Т.е. я понимаю так, что должен быть поток, который бы запускал все остальные и проверял : все законченно или нет.
Да. Но тут можно проще.
http://docwiki.embarcadero.com/RADSt...amming_Library
Создаёшь анонимную функцию и вызываешь в параллельном цикле. После параллельного цикла делаешь обычный цикл и включаешь все галочки.

Пример выше это очень мощное средство сокращает код в 1000 раз.
Паралельный цикл сам создает и уничтожает потоки. И плюс будет проверяет завершились они или нет. Т.е. на каждую итерацию создаёт по потоку и итерации будут вызываться параллельно*. А основной поток он всегда есть, его создавать не надо.
*параллельно- реально или псевдопаралельно.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .

Последний раз редактировалось Pavia; 24.08.2016 в 20:15.
Pavia вне форума Ответить с цитированием
Старый 24.08.2016, 21:03   #7
tarakan1983
Форумчанин
 
Аватар для tarakan1983
 
Регистрация: 09.09.2008
Сообщений: 418
По умолчанию

Цитата:
Сообщение от Pavia Посмотреть сообщение
tarakan1983
Да. Но тут можно проще.
http://docwiki.embarcadero.com/RADSt...amming_Library
Создаёшь анонимную функцию и вызываешь в параллельном цикле. После параллельного цикла делаешь обычный цикл и включаешь все галочки.

Пример выше это очень мощное средство сокращает код в 1000 раз.
Паралельный цикл сам создает и уничтожает потоки. И плюс будет проверяет завершились они или нет. Т.е. на каждую итерацию создаёт по потоку и итерации будут вызываться параллельно*. А основной поток он всегда есть, его создавать не надо.
*параллельно- реально или псевдопаралельно.
У меня теперь вообще мозг взорвался!!!!!!
Если не затруднит можете построчный анализ ЭТОГО
написать и объяснить на палочках (красненьких, зелененьких... как в первом классе )
tarakan1983 вне форума Ответить с цитированием
Старый 25.08.2016, 00:38   #8
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

Цитата:
написать и объяснить на палочках (красненьких, зелененьких... как в первом классе
Очистите свой мозг от того что знали раньше. И поверьте в верность свойств вышей математик. Сейчас я вас научу так называемому "абстрактному мышлению". Далее речь пойдет о использование свойств и методов объекта без погружение, без вникания в его устройство или происхождение.
Самое трудное это сделать первые шаги. Вам не нужно знать почему так, а не иначе. Вы должны воспринимать свойства и методы как аксиомы геометрии или законы механики.
Второй шаг тоже важен. Ваш мозг будет цепляться за соломинки искать аналогии, метафоры или напротив на основе частных предположений строить общие свойства.
Тут надо перебороть себя и перестать упрощать. Для этого надо в точности зачитывать описания свойства объектов. До тех пор пока свойства не отпечатаются у вас в мозгу и не будут отскакивать от зубов.

Запоминаем анонимная функция:
- не имеет имени;
- объявляется и описывается в коде как арифметическое выражение, а не в секции деклорации(interface) или реализации(implementation).
- является объектом! Т.е. её можно передать как параметр в функцию. А также присваивать методу класса.

Синим цветом я выделил анонимную процедуру, также можно объявить анонимную функцию.
Код:
type
  TMyProc=reference to procedure (I:Integer);
procedure Main;
var
 MyProc:TMyProc;
begin
// Присваиваем псевдониму анонимную функцию
MyProc:=procedure (I: Integer)
  begin
   WriteLn(I);
  end // конец анонимной функции
  ; // конец выражения
MyProc(0); // Вызов анонимной функции
end;
-------------------
Статические методы класса.
Статические методы класса можно вызывать не создавая объект.


Код:
program Demo2;
uses System.SyncObjs;
var count:Integer;
begin
count:=0;
TInterlocked.Increment(count);
WriteLn(count);
end.
Тут объект TInterlocked выполняет метод Increment(counter);
Метод аналогичен коду ниже
Код:
program Demo3;
var count:Integer;
begin
count:=0;
count:=count+1;
WriteLn(count);
end.
TInterlocked используется для синхронизации.

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

1) По-данным:
1.1) Атомарные операции, которые выполняются не разрывно.
1.2) Мьютекс
2) По-коду:
2.1) Критическая секция.
2.2) Светофорах.
3) По-времени:
3.1) Разнесённые обращения во времени. Карусель (RoundRobin).
3.2) На событиях. К примеру WaitAll или WaitAny

Этот список далеко не полный: есть спин-локи, защищенные очереди, сообщения и тд.

Как обезопасить переменные? Локальные переменные и параметры функций в защите не нуждаются так как располагаются в разных участках памяти. Поэтому обращения к ним не пересекается.
Общие переменные для потоков должны быть защищены. А также ввод и вывод он тоже должен быть безопасным. VCL не безопасен программист должен сам позаботиться об этом.
TInterlocked применяется для синхронизации по-данным.

Код:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils, System.Threading, System.SyncObjs;

var
 HalfSum:Integer;
procedure ParallelSum(i:Integer);
begin
 HalfSum:=HalfSum+1;
 Sleep(10);
end;
var j:Integer;
begin
  HalfSum:=0;
  TParallel.For(0, 100, ParallelSum);
  WriteLn(HalfSum);
end.
Выдает неправильный результат 93
изменяем на TInterlocked.Increment получаем правильную сумму 101

TParallel.For использует пул-потоков для автоматического создания и уничтожения потоков. Много потоков плохо сказывается на производительности. Мало тоже. Вот пул-потоков время от времени определяет производительности и создает или уничтожает потоки.

Каждая итерация цикла выполняется параллельно во-времени. В переменную I передаётся номер цикла.

Далее хотелось бы рассказать про шаблоны-программ работающих параллельно. А вот вопросы оптимизации связанные напрямую с устройством объекты синхронизации оставить на потом.

Я выделяю следующие шаблоны:
1) Толкучка.
2) Карусель.
3) Бос-рабочие
4) Рабочий-диспетчер
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .

Последний раз редактировалось Pavia; 25.08.2016 в 10:00.
Pavia вне форума Ответить с цитированием
Старый 25.08.2016, 09:59   #9
Pavia
Лис
Старожил
 
Аватар для Pavia
 
Регистрация: 18.09.2015
Сообщений: 2,409
По умолчанию

Как получить результат и как синхронность выполнения при вызове TParallel.For ?
За синхронность отвечает компилятор вернее библиотека System.Threading. Тут о чём либо думать не стоит.

А вот чтобы получить результаты, предлагаю ознакомиться с примером.

Код:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils, System.Threading, System.SyncObjs;

var
 ResultParsing:array of Integer;

procedure ParallelGetPage(i:Integer);
begin
  // Боевой код не привожу, так как ботов обычно баня за сильную активность.
 // Далее имитация клиента
 Sleep(Random(1000)); // Разное время загрузки страницы
 {тут должен парсинг}
 ResultParsing[i]:=Random(100); // Тоже имитируем случайным числом. 
end;

var j:Integer;
begin
  SetLength(ResultParsing,101);
  TParallel.For(0, Length(ResultParsing)-1, ParallelGetPage); // скорость выполнения определяет самая медленная операция

  Sum:=0;
  for j:=0 to Length(ResultParsing)-1 do
    Sim:=Sum+ResultParsing[i];
  WriteLn(Sum);
end.
Так как каждый поток использует свою переменную, то проблем с одновременной записью нет. Но как программа узнает когда потоки закончат работать?
Тут используется так называемая барьерная синхронизация. Все ездили на машинах и видели как работает шлагбаум? Представте себе закрытый шлагбаум на переезде в четырёхполосной дороге. Он и есть такой барьер. На разных полосах машины подъезжают к шлагбауму в разное время. А после когда барьер открывается все стартуют одновременно.

Так и тут время работы разных потоков разное. Но синхронизируются они в конце параллельного-цикла. Цикал ждёт пока самый медленный поток не закончит работать. Как только закончит барьерная блокировка снимается. И начинает работать основной поток.
В нём обычным последовательным циклом мы собираем результаты.
Код:
 for j:=0 to Length(ResultParsing)-1 do
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
У дзен программиста программа делает то что он хотел, а не то что он написал .
Pavia вне форума Ответить с цитированием
Старый 25.08.2016, 13:58   #10
tarakan1983
Форумчанин
 
Аватар для tarakan1983
 
Регистрация: 09.09.2008
Сообщений: 418
По умолчанию

Цитата:
Сообщение от Pavia
...
Сильно конечно, такого я нигде не читал. Спасибо большое!!!! Но с первой минуты после прочтения много непонятного, сейчас начну тестировать и разбираться. Это именно то что мне надо. Еще раз огромнейшее спасибо!
tarakan1983 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Синхронизация потоков _ZixeL_ Общие вопросы Delphi 14 10.09.2015 22:23
Синхронизация потоков _Bers Общие вопросы C/C++ 5 23.12.2011 22:57
Синхронизация потоков добрый_фей Помощь студентам 5 09.12.2011 19:57
Синхронизация потоков kardinal94 Общие вопросы Delphi 5 29.11.2010 21:13