Форум: "Начинающим";
Текущий архив: 2018.03.04;
Скачать: [xml.tar.bz2];
ВнизСоздание потоков Найти похожие ветки
← →
Макака (2016-03-15 15:48) [0]Нужно создать 4 потока
Я правильно понял, для этого достаточно создать 4 класса TThread ?
И правильно ли я понял, что эти 4 потока будут выполняться на разных ядрах?
← →
Rouse_ © (2016-03-15 15:51) [1]Как правило да, но можно системе подсказать как их лучше раскидать вызовом SetThreadAffinityMask(), правда это не значит что она так их распараллелит, только если будет возможность
← →
Макака (2016-03-15 16:55) [2]Допустим у меня есть цикл от 1 до 100 с кодом внутри и я хочу разбить его выполнение на 4 потока.
Этот код выношу в отдельную процедуру, в которой на входе указывается 2 числа: от какого до какого выполнять.
Далее создаю 4 потока и в коде Execute каждого потока вставляю ссылку на эту процедуру (например Run(1,25); во втором Run(26,50);
Правильно?
← →
Rouse_ © (2016-03-15 16:59) [3]можно и так
← →
Макака (2016-03-15 17:03) [4]Далее, у меня в коде цикла записано изменение Label-ов на форме, но почему то меняется только одна группа из рандомного потока.
Теперь мне получается нужно синхронизировать каждый потом с главным потоком проги?
← →
Юрий Зотов © (2016-03-15 17:23) [5]> для этого достаточно создать 4 класса TThread ?
Класс - один. Экземпляров этого класса - четыре.
> синхронизировать каждый потом с главным потоком
Только обновление Label"ов - см. Synchronize.
← →
Макака (2016-03-15 17:50) [6]Мне нужно в процедуре Update изменять значения Labe-ов ?
Synchronize(Update);
Или их можно в цикле(который выполняет поток) изменить, а потом обновить в Update?
← →
sniknik © (2016-03-15 18:05) [7]если хочешь "счетчик" то в цикле, если конечный результат вывести то потом.
← →
Макака (2016-03-15 18:27) [8]Сотрите
Основной код-цикл от 1 до 200, в нем изменяются Лейбелы, допустим так Label[i].Caption:=inttostr(i);
Запускается 4 потока
type
TNewThread = class(TThread)
I1,I2:integer;
procedure SetSynchronize;
protected
procedure Execute; override;
end;
var
Thread_1:TNewThread;
Thread_2:TNewThread;
Thread_3:TNewThread;
Thread_4:TNewThread;
...........................
procedure TNewThread.SetSynchronize;
begin
end;
procedure TNewThread.Execute;
begin
Run(I1,I2);
Synchronize(SetSynchronize);
end;
........................................
Thread_1:=TNewThread.Create(true);
Thread_1.I1:=0;
Thread_1.I2:=43;
Thread_1.FreeOnTerminate:=true;
Thread_1.Priority:=tpHighest;
Thread_1.Resume;
//Итак со всеми 4мя.
В I1 I2 указываются границы работа цикла.
Что делаем в SetSynchronize?
Как я думаю: нам нужно объявить список в TNewThread, после того как первый поток прошёл код в цикле до конца, передавать список в TNewThread, и далее в SetSynchronize по списку обновлять TLable
← →
Rouse_ © (2016-03-15 18:45) [9]Проект заархивируй и ссылку дай, так будет проще
← →
Юрий Зотов © (2016-03-15 18:54) [10]В поток надо передать ссылку на форму. Через эту ссылку поток получает доступ к форме и может делать с ней что угодно.
type
TNewThread = class(TThread)
Form: TMyForm;
... // Далее как у Вас
end;
procedure TNewThread.SetSynchronize;
begin
Form.Label1 := "Обновлено";
end;
Thread_1:=TNewThread.Create(true);
Thread_1.Form := Self;
... // Далее как у Вас
← →
Макака (2016-03-15 19:09) [11]Ругается на Self
← →
Юрий Зотов © (2016-03-15 19:23) [12]Этот код должен быть в методе формы, из которой создаются потоки (и на которой лежат Label"ы.
← →
Rouse_ © (2016-03-15 19:55) [13]На вот - с тебя моих потраченных 7 минут.
http://rouse.drkb.ru/tmp/thread.zip
← →
Rouse_ © (2016-03-15 20:07) [14]Ах, да забыл, там тебе нитей нужно было, тогда вот этот участок напиши вот так:
{ TNewThread }
var
ThreadIndex: Byte = 0;
constructor TNewThread.Create(AOwnerLabel: TLabel; A, B: Integer);
begin
inherited Create(False);
FA := A;
FB := B;
FOwnerLabel := AOwnerLabel;
FreeOnTerminate := True;
Priority := tpHigher;
SetThreadAffinityMask(Handle, 1 shl ThreadIndex);
end;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered := True;
TNewThread.Create(Label1, 0, 25000);
Inc(ThreadIndex);
TNewThread.Create(Label2, 26000, 50000);
Inc(ThreadIndex);
TNewThread.Create(Label3, 51000, 75000);
Inc(ThreadIndex);
TNewThread.Create(Label4, 75000, 100000);
end;
← →
Игорь Шевченко © (2016-03-15 22:00) [15]В Demos не судьба посмотреть ?
← →
Макака (2016-03-16 00:02) [16]Юрий Зотов большое спасибо! Завтра посмотрю твой код.
Короче, я пришёл к таких выводам:
если создать 1 поток всё работает! и к компонентам формы можно обращаться, можно делать всё что угодно.
Если создать два потока, то работает один из них. Может это в вин10 что то?
← →
Германн © (2016-03-16 00:58) [17]
> Короче, я пришёл к таких выводам:
> если создать 1 поток всё работает! и к компонентам формы
> можно обращаться, можно делать всё что угодно.
> Если создать два потока, то работает один из них. Может
> это в вин10 что то?
>
Неправильные выводы. Ну т.е. абсолютно неправильные.
"1 поток" у тебя всегда создан, раз ты написал программу и запустил её. И к компонентам формы можешь обращаться из него без каких-либо ограничений.
Проблемы с дополнительными потоками. Сколько бы их не было. 1, 2, 3 и т.д.
← →
Макака (2016-03-16 01:36) [18]Либо с вероят 90 проц я не правильно распараллелил код (хотя в нём всё выглядит логичным). Единственное в чём я не уверен: создал класс(свой), создал несколько экземпляров, заполнил первый данными, копировал так:
Экземпляр2:= Экземпляр1, этого же достаточно для копирования?
← →
Макака (2016-03-16 01:49) [19]У меня нет в потоках обращения к одним и тем же VCL компонентам, поэтому синхронизация мне не нужна, поэтому 100 процентов дело в распараллеливании.
← →
popcorn++ (2016-03-16 03:14) [20]Удалено модератором
← →
sniknik © (2016-03-16 08:38) [21]> У меня нет в потоках обращения к одним и тем же VCL компонентам, поэтому синхронизация мне не нужна,
тебе может и нет, а программе нужна.
> поэтому 100 процентов дело в распараллеливании.
дело всегда в прокладке.
← →
Юрий Зотов © (2016-03-16 09:22) [22]> Макака (16.03.16 01:36) [18]
> создал несколько экземпляров, заполнил первый данными, копировал так:
> Экземпляр2:= Экземпляр1, этого же достаточно для копирования?
Нет. Любая объектная переменная по сути есть указатель, ссылка на объект. Сам объект лежит где-то в динамической памяти, а ссылка лишь хранит его адрес.
Поэтому Вы копируете не объект, а его адрес. То есть, обе переменные (и Экземпляр1, и Экземпляр2) у Вас указывают на один и тот же поток.
← →
Rouse_ © (2016-03-16 10:09) [23]
> Макака (16.03.16 01:49) [19]
Ну ты код для начала посмотри, который я тебе написал выше, все вопросы отпадут.
← →
Макака (2016-03-16 12:19) [24]Всё получилось, когда правильно скопировал экземпляры класса.
← →
Макака (2016-03-16 12:20) [25]Всем спасибо!
← →
Макака (2016-03-16 14:15) [26]Ещё такой вопрос, есть длл, можно её запустить в 4 разных потоках?
← →
Юрий Зотов © (2016-03-16 14:27) [27]> Макака (16.03.16 14:15) [26]
1. Запускается не DLL, а функции из нее.
2. Можно, если в DLL нет специальных препятствий для этого.
← →
Макака (2016-03-16 14:29) [28]создал 4 дллки, и 4 отедльных функции на экспорт в каждой, только так сработало
← →
Макака (2016-03-16 15:08) [29]А как сделать, чтобы после завершения работы всех 4ёх потоков выполнялся код?
← →
Inovet © (2016-03-16 15:14) [30]> [29] Макака (16.03.16 15:08)
Обяснили же: есть основной поток, который запускает остальные. Вот он пусть ждёт завершения и дальше делает ещё что-то.
← →
Inovet © (2016-03-16 15:15) [31]С отдельной DLL на каждый поток - это какой стиль? По-моему китайский.
← →
Макака (2016-03-16 15:24) [32]Inovet это я прекрасно понял) а как это сделать?
Можно написать так Sleep(замерить макс время выполнения и написать сюда);
пойду погуглю ещё
← →
Inovet © (2016-03-16 15:32) [33]> [32] Макака (16.03.16 15:24)
> а как это сделать?
WaitForMultipleObjects
← →
Макака (2016-03-16 16:54) [34]WaitForMultipleObjects и как он работает?)
делаю так, на 4 потока:
var Handles : array of Cardinal;
..................
SetLength(Handles,4);
Thread_1:=TNewThread.Create(true);
Handles[0]:=Thread_1.Handle;
Thread_1.FreeOnTerminate:=true;
Thread_1.Priority:=tpNormal;
Thread_1.I1:=0;
Thread_1.I2:=51;
Thread_1.TreadNumb:=0;
Thread_1.Resume;
...............................итак со всеми
if (WaitForMultipleObjects(4,@Handles,true,infinite) = 1) then
ShowMessage("Всё, приехали!");
← →
Юрий Зотов © (2016-03-16 17:20) [35]Handles - это, по сути, указатель. Поэтому @Handles - это адрес указателя, но не массива. А вот @Handles[0] - это адрес массива.
← →
Inovet © (2016-03-16 17:40) [36]> [34] Макака (16.03.16 16:54)
> if (WaitForMultipleObjects(4,@Handles,true,infinite) = 1)
Зачем 1? Вот здесь смотри символьные значения возврата
https://msdn.microsoft.com/en-us/library/windows/desktop/ms687025%28v=vs.85%29.aspx
← →
Макака (2016-03-16 18:07) [37]
if (WaitForMultipleObjects(4,@Handles[0],true,infinite) = WAIT_OBJECT_0) then
ShowMessage("Всё, приехали!");
Молчит
← →
Макака (2016-03-16 18:21) [38]изменил тип массива на
Handles : array of THandle;
заговорила)
Но на этом приключения не закончились, до момента "приехали" виснет приложение.
← →
Макака (2016-03-16 18:22) [39]нужно ещё один поток создавать?
← →
sniknik © (2016-03-16 18:37) [40]с точностью до наоборот... попытайся обойтись, продумай логику, одним. а лучше вообще без потоков.
— Эх, эх... — сказал гость, морщась.
— А вам, что же, мои стихи не нравятся? — с любопытством спросил Иван.
— Ужасно не нравятся.
— А вы какие читали?
— Никаких я ваших стихов не читал! — нервно воскликнул посетитель.
— А как же вы говорите?
— Ну, что ж тут такого, — ответил гость, — как будто я других не читал? Впрочем... разве что чудо? Хорошо, я готов принять на веру. Хороши ваши стихи, скажите сами?
— Чудовищны! — вдруг смело и откровенно произнес Иван.
— Не пишите больше! — попросил пришедший умоляюще.
— Обещаю и клянусь! — торжественно произнес Иван.
← →
Макака (2016-03-16 19:09) [41]можно, наверное, через таймер сделать. Создать переменную, и увеличивать её на 1 если поток завершён.
← →
Rouse_ © (2016-03-16 19:16) [42]Ты код свой показать целиком можешь? А то сиди тут гадай на кофейной гуще в какую лужу ты вступил
← →
sniknik © (2016-03-17 08:40) [43]а как же тогда интрига? долгие псевдоинтелектуальные вопросы ни о чем? не, код не нужен.
- А Скрипач не нужен, родной. Он только лишнее топливо жрёт.
← →
Юрий Зотов © (2016-03-17 10:11) [44]> до момента "приехали" виснет приложение
Оно не виснет. Оно ждет завершения всех потоков. Вы же сами вызвали WaitForMultipleObjects - вот оно и ждет.
← →
Юрий Зотов © (2016-03-17 10:22) [45]А чтобы не "висло", можно сделать так - вместо INFINITE поставить разумный интервал (например, 1 сек) и вызывать WaitForMultipleObjects в цикле:
while WaitForMultipleObjects(4, @Handles[0], true, 1000) = WAIT_TIMEOUT do
Application.ProcessMessages;
← →
sniknik © (2016-03-17 10:57) [46]> while WaitForMultipleObjects(4, @Handles[0], true, 1000) = WAIT_TIMEOUT do
> Application.ProcessMessages;
???
не учите плохому... он же именно так и "собезьяничает".
если появилась нужда в подобном коде, то смысл вообще в потоке/потоках? просто делай действие прямо там, в основном. время выполнения и будет "ожиданием". ну подвиснет интерфейс чуток, ну и что?
поток нужен если требуется параллельно что-то делать, ждать в это "что-то" не входит.
"проблемы" с интерфейсом несущественны, только ради этого "эстетство" разводить - впустую тратить время.
← →
Юрий Зотов © (2016-03-17 11:29) [47]> sniknik © (17.03.16 10:57) [46]
Не вижу ничего плохого. Это не таймер, который бессмысленно расходует процессорное время, а нормальное ожидание завершения всех задач.
> если появилась нужда в подобном коде, то смысл вообще в потоках?
> поток нужен если требуется параллельно что-то делать,
Именно. Есть N задач, которые могут выполняться параллельно, но приложение не должно продолжать свою работу, пока не завершатся все эти задачи. Соответственно, главный поток порождает N вторичных потоков и ждет их завершения.
Похоже на задачу массового обслуживания: в магазине есть N продавцов и магазин не должен закрываться, если хотя бы один продавец еще занят.
Пример из реальной практики. Невизуальное приложение обрабатывает информацию от нескольких терминалов сбора данных. Главный поток слушает подключение терминала и, обнаружив его, порождает поток обработки данных от этого терминала, а сам продолжает прослушку.
Поступает команда на завершение работы - но ее нельзя завершать, пока не будут полностью обработаны все данные от всех уже подключенных терминалов. Тогда, получив команду на завершение, главный поток снимает прослушку, перестает порождать новые потоки, дожидается завершения всех уже запущенных, а потом завершается сам.
Приблизительно по этой схеме (на самом деле сложнее, но это сейчас неважно) была написана служба для одного из подмосковных предприятий.
← →
эндсоувот © (2016-03-17 11:36) [48]а нормальное ожидание завершения всех задач.
нормальное будет через MsgWaitForMultipleObjects
и если главный поток будет делать хоть что-нибудь полезное кроме ожидания.
Если ждать - потоки не нужны.
Если потоки нужны, то их не надо ждать.
Расскажите им про хендл нужного окна и потоки сами скажут когда они завершили работу.
← →
Юрий Зотов © (2016-03-17 11:46) [49]> через MsgWaitForMultipleObjects
Согласен, но цикл и ProcessMessages при этом все равно останутся.
> хендл нужного окна
1. Окна может и не быть.
2. Потребуется синхронизация.
← →
эндсоувот © (2016-03-17 12:01) [50]пример разумного использования потоков без ожиданий синхронайзов и прочих колхозов:
цб публикует данные для неких форм.
сервис устроен таким образом, что нельзя "одним запросом вынуть все".
можно только по частям, для которых есть ~три-четыре критерия дробления.
таким образом:
в главном инициируется структура с описанием "чего надо вынуть"
далее генерятся начальные скажем пять потоков, которые достают каждый по одной порции.
родившись, поток шлет сообщение окну в котором запрашивает параметры очередной порции данных, затем лезет в веб, получает данные, отдает их очереди sql-потока и дальше снова сигнализирует окну вопросом: "есть чо делать?"
если есть, то все повторяется, если нет, поток убивается об стену.
в это время юзер глядя на главную форму видит скачущий счетчик отработанных порций данных.
... и двигает ползунок, пробуя увеличить число экземпляров потоков.
если текущая ширина канала еще не выбрана, то юзер наглядно видит, что увеличение числа потоков ускоряет счетчик выполненных заданий.
если же полоса канала уже выбрана то счетчик не ускоряется.
кроме того, возможно адаптивно подстраивать число потоков и без участия юзера.
если теперь в эту грациозную, элегантную и совершенную схему впендюрить вэйты, то либо ничего хорошего не получится, либо будет такой спагетти-код, что атас, война и немцы.
← →
sniknik © (2016-03-17 12:14) [51]> 1. Окна может и не быть.
но управляющий процесс/поток то есть? т.что PostTreadMessage в общий канал управления
> а сам продолжает прослушку.
здесь и поймает
> 2. Потребуется синхронизация.
зачем? простой счетчик работающих потоков, инкримент перед стартом, декремент по сообщению о завершении из самого потока.
> Тогда, получив команду на завершение
либо завершается если счетчик работающих = 0, либо взводит "флаг" на завершение по которому
> перестает порождать новые потоки
и завершается по достижению счетчиком нуля.
никакой синхронизации, и ожиданий не требуется, все в стиле "событие -> реакция".
← →
Юрий Зотов © (2016-03-17 12:25) [52]> и завершается по достижению счетчиком нуля.
Вот именно ЭТО и означает: ЖДАТЬ обнуления счетчика.
← →
эндсоувот © (2016-03-17 12:29) [53]"ждать" понятие растяжимое.
юзер ничего не делающий и глазеющий на форму тоже как бы "ждет".
а вопрос лишь в том, что в это время происходит "под формой"
если там крутится цикл while wait.... то понятно, что сделать что-либо еще программист сейчас не может. хотя все кнопки и нажимются
а если цикла нет, то у нас доступно все
← →
sniknik © (2016-03-17 12:35) [54]> Вот именно ЭТО и означает: ЖДАТЬ обнуления счетчика.
нет, т.к. никаких функций ожиданий/псевдоожиданий([45]) в коде нет, есть обработчик событий с реакцией на них.
разница однако...
> в магазине есть N продавцов и магазин не должен закрываться, если хотя бы один продавец еще занят.
по этой, же аналогии:
ожидание: охранник закрывающий магазин "втыкает" на продавцов, ждет в общем (весело если кассы сильно разнесены).
событие: выключение последней кассы оповещает охранника (звонок в магазине), который может заниматься чем угодно вместо "втыкания", но по звонку идет и закрывает магазин.
← →
Макака (2016-03-17 17:38) [55]Смысл в том, что когда мы нажимаем на кнопку расчёты появляется полупрозрачная панелька с гифкой и надписью "ожидайте"(чтобы гифка не зависала, нужно чтобы форма не висла), далее запускаются 4 потока и производятся расчёты, после их завершения обновляются компоненты на форме и панель прячется.
Вчера реализовал по "колхозному" через таймер.
Сегодня буду разбираться что выше написано, понять как нужно сделать (без колхоза).
← →
Inovet © (2016-03-17 20:37) [56]> [55] Макака (17.03.16 17:38)
> далее запускаются 4 потока и производятся расчёты, после
> их завершения обновляются
Тогда надо
1. Отдавать потокам (нитям) порции небольшого размера, менее вся_порция/число_потоков.
2. В вызывающей нити распределять остатки, в разумных пределах, чтобы не уйти в бесконечность.;)
И тогда следует юзать
WaitForMultipleObjects
с анализом - кто там завершился и подкидкой ему новой порции.
← →
Eraser © (2016-03-17 21:08) [57]Ждать завершения потока/ов нужно в OnTerminate, который выполняется в контексте главного потока приложения.
Никакие таймеры и циклы ожидания тут не нужны, это лишнее для данной задачи. Лезть со своими "ожиданиями" в цикл выполнения основного потока допустимо только в случае крайней необходимости.
← →
NoUser © (2016-03-18 00:13) [58]> Лезть со своими "ожиданиями" в цикл выполнения основного
> потока допустимо только в случае крайней необходимости.
ага, например, мышь неожиданно жмякнула крестик.
Поэтому [45] учитывая [55] для ТС самое оно.
> [54] > событие: выключение последней кассы
а кто определит, что она последняя?
← →
sniknik © (2016-03-18 01:01) [59]сама и определит, ее же закрывают, снимают z-отчет. что само по себе событие... выше про счетчик почитай.
не нужно плодить сущности.
Страницы: 1 2 вся ветка
Форум: "Начинающим";
Текущий архив: 2018.03.04;
Скачать: [xml.tar.bz2];
Память: 0.61 MB
Время: 0.003 c