Синхронизация потоков

  • Автор темы tarakan19832
  • 3648
  • Обновлено
  • 24, Aug 2016
  • #1
Подскажите, пожалуйста, на моем примере как правильно синхронизировать несколько потоков. На форме есть 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’ы. Подскажите как это осуществить?

tarakan19832


Рег
12 Aug, 2016

Тем
3

Постов
7

Баллов
37
  • 31, Aug 2016
  • #2
Создай несколько экземпляров потока. Передай в каждый поток указатель на свой комбобокс. В потоке заполняй комбобокс и enable его. Запускай все потоки одновременно.
 

dustypup


Рег
27 Nov, 2008

Тем
0

Постов
8

Баллов
8
  • 26, Sep 2016
  • #3
synchronize используется для GUI, для синхронизации действий, лучше использовать симофоры, пока действие не будет сделано семафор дальше не пустит:

SemaphoreTask : THandle;

.....

SemaphoreTask := CreateSemaphore(nil, 0, 2147483647, '');

.....

// Эта команда WaitForSingleObjectEx будет ожидать с любого участка программы --> ReleaseSemaphore(SemaphoreTask, 1, nil); WaitForSingleObjectEx(SemaphoreTask , INFINITE, True); .... // закрыть семафор CloseHandle(SemaphoreTask ); Таким образом можешь синхронизировать любый задачи!
 

fets


Рег
04 Oct, 2011

Тем
1

Постов
6

Баллов
16
  • 29, Sep 2016
  • #4
Earl_Vadim, post: 767370:
Есть такое простое слово как mutex - оно все объясняет.
Я понимаю, Вы профессионал и это слово Вам все объяснят, но мне не очень. Не могли бы Вы вкратце с примерчиком? Или дайте ссылку на пример.
 

tarakan1983


Рег
30 Jul, 2016

Тем
0

Постов
5

Баллов
5
  • 29, Sep 2016
  • #5
Нет. я не профессионал.. все сам ищу через поиск. Суть mutex - тотже симафор... просто поиском вы больше инфы найдете по слову mutex, чем по слову Semaphore .. а вообще более полного и более правильного описания потоков чем это вы не найдете.
 

Earl_Vadim


Рег
01 Aug, 2015

Тем
0

Постов
2

Баллов
2
  • 01, Oct 2016
  • #6
По вопросам синхронизации фундаментальный труд - Джеффри РИХТЕР "Создание эффективных WIN32-приложений с учётом специфики 64-разрядной версии Windows". В нем описаны все основные элементы WinAPI и особенности их использования (семафоры, мьютексы в том числе). Переложить на Delphi труда не составит.

В описанной задаче в потоке-синхронизаторе, на мой взгляд, необходимости нет.

Каждый поток может независимо от другого заполнять TStringList из базы.

Дальнейший перенос в TComboBox осуществляется с помощью Synchronize.

Этот метод сам по себе не даст другим потокам обратиться к основному потоку Vcl, пока какой-либо пишет данные в TComboBox.
 

alchemist131


Рег
16 Nov, 2010

Тем
0

Постов
5

Баллов
5
  • 06, Oct 2016
  • #7
Если у тебя доступ к базе данных на чтение открыт свободно, я бы сделал потоки для каждого комбобокса: hThr = CreateThread(NULL, 0, ThreadProcName, pCombobox, 0, & dwThrID);
В потоке читал бы базу и обновлял содержимое формы.
Если хочешь включить сразу все комбобоксы пусть каждый поток выставит Event по окончании работы, а основной поток пусть ждёт все эти Events через WaitforMultipleObjects.

только я настоятельно рекомендую не писать INFINITE, чтобы не вешать поток, лучше взять заведомо большее время, за которое точно должны сработать потоки (60000 мс например).
DWORD rez = WaitforMultipleObjects(iCheckBoxNumber, evThreadHadDoneItsWork, TRUE, 60000);
if(rez == WAIT_TIMEOUT)
{
// Ждали минуту потоки не завершились
}
else if (rez == WAIT_OBJECT_0) {включить все комбы}
 

Keoda


Рег
19 Nov, 2014

Тем
1

Постов
18

Баллов
28
  • 07, Oct 2016
  • #8
это все многопоточная чушь в итоге со всеми синхронизациями и прочей фигней результат будет тот же, что и без потоков + куча лаганого ненужного говнокода выключил все сразу, заполнил и включил
 

robt


Рег
29 Apr, 2015

Тем
1

Постов
3

Баллов
13
  • 08, Oct 2016
  • #9
Эм, а разве нельзя использовать критические секции? Всегда ими пользовался когда нужно обратиться к форме. Либо использовать глобальные массивы по окончанию работы из которых заполнять форму. или по таймеру например. Ведь куча вариантов.
 

Kelvin21


Рег
06 Oct, 2016

Тем
0

Постов
7

Баллов
7
  • 12, Oct 2016
  • #10
Вообще, множественная работа с GUI в потоках - тот еще геморрой, соглашусь с комментатором №11
 

ForumReader


Рег
16 Oct, 2008

Тем
0

Постов
10

Баллов
10
  • 12, Oct 2016
  • #11
с гуи вообще лучше не работать или выносить в основной поток и синхронизировать раз в хх секунд с формой
 

йцукеурщл


Рег
30 May, 2014

Тем
0

Постов
9

Баллов
9
  • 14, Oct 2016
  • #12
вариантов синхронизировать потоки несколько, и каждый используется по ситуации, рекомендую книгу Стив Тейксейра, Ксавье Пачеко "Delphi 5, Руководство разработчика. Том 1. Основные методы и технологии". Хоть и для старой делфи, но принципы остались теже
 

tell5


Рег
06 Mar, 2015

Тем
2

Постов
34

Баллов
54
  • 15, Oct 2016
  • #13
VCL, насколько я понимаю, однопоточный и из разных потоков в данной задаче работать с ним смысла наверное нат.

Заполняться они наверняка будут по-очереди.

Непонятно почему комбобоксы заполняются долго - сложная выборка из БД или очень много элементов?

Если БД, то несколько потоков могут БД сделать только хуже.

Если много данных, то правильнее будет загружать данные по мере необходимости в комбобоксы, используя какой-то поиск что-ли.
 

protos59


Рег
15 Sep, 2016

Тем
0

Постов
11

Баллов
11
  • 16, Oct 2016
  • #14
Многие DAC позволяют получать данные из БД в потоках параллельно, нужно только каждому запросу предоставить отдельный коннекшн. Никаких проблем не наблюдал.
 

styxozavr


Рег
25 Jul, 2009

Тем
0

Постов
2

Баллов
2
  • 16, Oct 2016
  • #15
нужно только каждому запросу предоставить отдельный коннекшн
очень смешно это кто додумался до такого? для многопоточных запросов нужно в самом query выставить тип Asynchrone или в одном подключении запустить многопоточный TTHeread 1 connection - 1 запрос это очень не то
 

йцукеурщл


Рег
30 May, 2014

Тем
0

Постов
9

Баллов
9
  • 17, Oct 2016
  • #16
йцукеурщл, post: 767382:
это кто додумался до такого?
цитата:
A connection object and all objects associated with it (such as TFDQuery, TFDTransaction, and so on) are used by a single thread at each moment.
Иными словами: для того, чтоб запросы работали асинхронно, необходимо каждому предоставить свой коннект.
 

styxozavr


Рег
25 Jul, 2009

Тем
0

Постов
2

Баллов
2
  • 23, Oct 2016
  • #17
йцукеурщл, post: 767382:
очень смешно
это кто додумался до такого?

для многопоточных запросов нужно в самом query выставить тип Asynchrone
или в одном подключении запустить многопоточный TTHeread

1 connection - 1 запрос это очень не то
А что не так? Речь идет о параллельном выполнении. Известные мне СУБД не умеют через одно соединение выполнять одновременно несколько запросов. Со стороны delphi можно использовать пул соединений, который скроет создание нескольких соединений.
 

protos59


Рег
15 Sep, 2016

Тем
0

Постов
11

Баллов
11
  • 24, Nov 2016
  • #19
обявляешь CRITICAL_SECTION _USEVCL; .................. .................. инициализируешь InitializeCriticalSection( & _USEVCL); потом пользуешься в каждом потоке.

Обрамляешь конкурирующие действия в EnterCriticalSection и LeaveCriticalSection. EnterCriticalSection( & __USEVCL); критичные код LeaveCriticalSection( & __USEVCL); тут главное не забывать делать leave.
 

Zhrnya


Рег
06 May, 2010

Тем
0

Постов
4

Баллов
4
  • 24, Nov 2016
  • #20
+1
protos59, post: 767384:
А что не так? Речь идет о параллельном выполнении. Известные мне СУБД не умеют через одно соединение выполнять одновременно несколько запросов. Со стороны delphi можно использовать пул соединений, который скроет создание нескольких соединений.


Поддерживаю я пока лично так и делаю пока по другому не умею.

может кто пример даст???

procedure TNewThread.Execute;

var

q_regupdate,q_region : TUniQuery;

ThreadConn, ThreadConn2 : TUniConnection;

begin

CoInitialize(nil);

q_region:= TUniQuery.Create(nil);

q_regupdate:= TUniQuery.Create(nil);

ThreadConn := TUniConnection.Create(nil);

ThreadConn2 := TUniConnection.Create(nil);

with ThreadConn do

begin

LoginPrompt := false;

Server := '';

Username := '';

Password := '';

Port := ;

ProviderName := '';

Database := '';

end;

with ThreadConn2 do

begin

LoginPrompt := false;

Server := '';

Username := '';

Password := '';

Port := ;

ProviderName := '';

Database := '';

end;

q_region.Connection := ThreadConn;

q_regupdate.Connection := ThreadConn2;

//Выполнение запроса

q_region.SQL.Clear;

q_region.SQL.Add(';

q_region.Open;

Main_form.cxProgressBar2.Properties.Max := q_region.RecordCount;

while not q_region.Eof do

begin

q_regupdate.SQL.Clear;

q_regupdate.SQL.Add('');

q_regupdate.ExecSQL;

Synchronize(update);

q_region.Next;

end;

FreeAndNil(q_regupdate);

FreeAndNil(q_region);

FreeAndNil(ThreadConn);

FreeAndNil(ThreadConn2);

CoUninitialize;

Synchronize(update);

end;
 

arendaplus


Рег
28 May, 2007

Тем
0

Постов
17

Баллов
17
  • 06, Dec 2016
  • #21
Я попробую предложить самый простой вариант. В модуле формы локальную переменную-счетчик. при старте ставим его в 0. В "Синхронайз" каждого потока увеличивать этот счетчик и проверять, если он равен количеству комбобоксов сделать все комбо доступными.
 

test1c


Рег
25 Jul, 2010

Тем
1

Постов
23

Баллов
33
  • 06, Dec 2016
  • #22
The fastest synchronization for an executable is the TCriticalSection of SyncObj. Guaranteed to be faster than semaphores or mutexes. We recommend that you look at several ways to synchronize.
 

yyjksw


Рег
17 Jul, 2008

Тем
0

Постов
13

Баллов
13
  • 11, Dec 2016
  • #24
зачем вообще трогать комбобоксы из потока, они все равно будут заполняться последовательно и висеть на время заполнения. в потоках собрать только данные, а потом на форме запустить заполнение
 

Ruslan82


Рег
05 Feb, 2011

Тем
2

Постов
22

Баллов
42
  • 15, Dec 2016
  • #25
tarakan19832, post: 767364:
Подскажите, пожалуйста, на моем примере как правильно синхронизировать несколько потоков.
На форме есть 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’ы. Подскажите как это осуществить?
Можно проще. Объявляшь глобально или полем формы var A: array of TStringList; В OnCreate формы SetLength(A, N); В i-том потоке A:=TStringList.Create; и далее A.Add();
В конце работы поток посылает форме сообщение WM_USER+100;
В обработчике WM_USER+100 формы просто считаешь количество пришедших сообщений. Как придет N штук, заполняешь комбобоксы данными из A.
 

delphialex


Рег
18 May, 2008

Тем
0

Постов
15

Баллов
15
  • 16, Dec 2016
  • #26
почитай еще про lockfree-структуры, они позволяют избежать блокирования между потоками
 

test-593


Рег
03 Dec, 2014

Тем
1

Постов
3

Баллов
13
  • 01, Jan 2017
  • #27
В основном потоке нужно задизэйблить все элементы UI, создать поток обновления для каждого элемента UI, каждый поток должен по окончанию послать событие на enable. Но комментатор N11 всё равно прав. Выиграш будет нулевой, в большинстве случаев станет только хуже.
 

dimonak90


Рег
14 Jul, 2009

Тем
0

Постов
4

Баллов
4
  • 27, Mar 2017
  • #28
test1c, post: 767389:
Я попробую предложить самый простой вариант.
В модуле формы локальную переменную-счетчик. при старте ставим его в 0.
В "Синхронайз" каждого потока увеличивать этот счетчик и проверять, если он равен количеству комбобоксов сделать все комбо доступными.
При использовании такого способа следует указать атрибут Volatile для счётчика и можно будет обойтись без Synchronize для работы с ним:
http://docwiki.embarcadero.com/RADStudio/Berlin/en/Compiler_Attributes#Volatile

В свежих версиях Delphi появились библиотека Parallel Programming Library, которая упрощает запуск нескольких заданий в отдельных потоках и их синхронизацию с основным потоком:
http://docwiki.embarcadero.com/RADStudio/Seattle/en/Using_the_Parallel_Programming_Library

http://docwiki.embarcadero.com/RADStudio/Seattle/en/Using_TTask_from_the_Parallel_Programming_Library
 

vegebi


Рег
22 Mar, 2017

Тем
0

Постов
5

Баллов
5
  • 06, Apr 2017
  • #29
tarakan19832, post: 767364:
Подскажите, пожалуйста, на моем примере как правильно синхронизировать несколько потоков.
На форме есть 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’ы. Подскажите как это осуществить?
Примерно тоже самое. В нескольких потоках заполняете tstringlist и через synchronize назначаете каждому свой через assign vcl - Однопоточная и работать с компонентами параллельно нельзя.
 

X-Cite!


Рег
24 Mar, 2014

Тем
1

Постов
15

Баллов
25
  • 09, Apr 2017
  • #30
С VCL вообще не стоит работать из других потоков даже со всякими там критическими секциями, семафорами, эвентами и т.д., т.к. у VCL обработка очереди сообщений идёт в своём (главном) потоке и никак с вашими самоделками не синхронизируется.

При такой работе можно словить странное поведение контролов. Если уж получать данные в несколько потоков, то стоит передавать в поток для чего и какие данные он получает и затем через Synchronize возвращать результат.

Как вариант можно класть результат в очередь и сигналить форме через PostMessage. С OmniThreadLirary и новыми классами для многопоточной работы из новых дельфей не работал, но можно там посмотреть что-то готовое на эту тему (Task-и там всякие, готовые пулы потоков и т.д.).
 

d_z


Рег
21 Jan, 2005

Тем
0

Постов
2

Баллов
2
  • 03, May 2017
  • #31
Какая версия Delphi? Если не очень старая, стоит посмотреть в сторону TTask - там легко управлять пулом потоков
 

falsenull


Рег
02 May, 2017

Тем
1

Постов
10

Баллов
20
  • 15, May 2017
  • #32
uses System.SyncObjs // ------------------------------ var FLocker: TCriticalSection; // ------------------------------ FLocker := TCriticalSection.Create; // ------------------------------ FLocker.Enter try // тут твои сихронизированные действия finnaly FLocker.Leave; end; // ------------------------------ FreeAndNil(FLocker);
 

alexgreat


Рег
14 Apr, 2006

Тем
0

Постов
3

Баллов
3
  • 09, Oct 2018
  • #33
tarakan19832:
Подскажите, пожалуйста, на моем примере как правильно синхронизировать несколько потоков.
На форме есть 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’ы. Подскажите как это осуществить?


Для кто нашел ветку, но не решил проблему)

Если хочешь поток, то создай компонент на основе adotable где во время выполнения потока на while ставишь sleep пока не выполнится, т.е не изменится dataset твой.

Идея реализации заполнения combobox через stringlist очень плоха на мой взгляд.

Использовать стоит lookupcombobox, тех же devexpress.
 

kefeka


Рег
28 Mar, 2016

Тем
1

Постов
6

Баллов
16
Тем
49554
Комментарии
57426
Опыт
552966

Интересно