Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2003.09.22;
Скачать: [xml.tar.bz2];

Вниз

Сколько потоков ??   Найти похожие ветки 

 
Прохожий   (2003-08-25 11:03) [0]

В цикле создаю потоки, которые после работы самоустраняються. Как бы мне узнать сколько потоков сейчас рабочих ? И как бы ограничить их число ?


 
Е-Моё имя   (2003-08-25 11:07) [1]

A semaphore object is a synchronization object that maintains a count between zero and a specified maximum value. The count is decremented each time a thread completes a wait for the semaphore object and incremented each time a thread releases the semaphore. When the count reaches zero, no more threads can successfully wait for the semaphore object state to become signaled. The state of a semaphore is set to signaled when its count is greater than zero, and nonsignaled when its count is zero.

The semaphore object is useful in controlling a shared resource that can support a limited number of users. It acts as a gate that limits the number of threads sharing the resource to a specified maximum number.


 
Прохожий   (2003-08-25 11:14) [2]

И где это бы глянуть ?


 
Е-Моё имя   (2003-08-25 11:19) [3]

Winдофс SDK
тема Semaphore Objects


 
y-soft   (2003-08-25 11:58) [4]

В общем-то если ипользуется TThread, то Delphi сама ведет такой учет и приложение не завершается, пока есть хотя бы один поток.

Для ограничения кол-ва действительно удобно использовать семафоры, но следует учитывать, что:

1. Если семафор занят, то поток ставится в очередь и ждет
2. Невозможно определить через семафор, не изменяя его внутренний счетчик, количество работающих потоков

На русском хорошая информация у Рихтера...


 
Camus   (2003-08-25 12:09) [5]

Есть вариант и проще - TThreadList


 
Sandman25   (2003-08-25 12:11) [6]

А еще можно использовать глобальную переменную с критической секцией - перед созданием проверять на возможность создания нового потока, в конструкторе потока переменную увеличивать, в деструкторе - уменьшать.


 
Nikky   (2003-08-25 12:21) [7]

что мешает thread-ы в какой-нить tList добавлять? это ж просто объекты


 
Camus   (2003-08-25 12:50) [8]

> Nikky © (25.08.03 12:21) [7]

В любой нельзя - нужен потокозащищенный. Это и есть TThreadList.


 
Прохожий   (2003-08-25 14:38) [9]

Sandman25 [6]
А это идея!!!


 
panov   (2003-08-25 14:53) [10]

Тогда уж еще проще
InterlockedIncrement
InterlockedDecrement


 
Palladin   (2003-08-25 16:05) [11]


> Camus © (25.08.03 12:50) [8]

ты не есть прав...
TThreadSafe это тот же TList с TCriticalSection...
потоки можно пихать и в TList и в TObjectList...
и термин потокозащищенность относится к обшепотоковым ресурсам а не к самим потокам...


 
Palladin   (2003-08-25 16:05) [12]

тьфу, не TThreadSafe а TThreadList


 
Прохожий   (2003-08-26 07:37) [13]

>y-soft
В общем-то если ипользуется TThread, то Delphi сама ведет такой учет и приложение не завершается, пока есть хотя бы один поток.
Кстати вопрос: " А как сбросить проек, если какой то поток ещё выполняеться? Мне приходиться вырубать делфи, а потом открывать заново :(" ?


 
MBo   (2003-08-26 07:40) [14]

штатно - установить Terminated и WaitFor


 
Прохожий   (2003-08-26 08:07) [15]

штатно - установить Terminated и WaitFor
А по подробнее ?


 
MBo   (2003-08-26 08:10) [16]

Thread.Terminate;
Thread.WaitFor;


 
Прохожий   (2003-08-26 08:18) [17]

Ты неверное не понял. Я имел в виду, что когда у меня, к примеру один поток вошёл в бесконечный цикл, я нажимаю "Resrt Program". Но сбрасываеться лишь основной поток, а тот - продолжает работать. Поэтому мне и приходиться выгружать делфи, а потом снова открывать прокт. Програмно то его остановить - это не вопрос, а вот в процесе отладки...???


 
y-soft   (2003-08-26 08:19) [18]

>Прохожий © (26.08.03 07:37) [13]

Нештатно - TerminateThread или даже TerminateProcess, в наихудших случаях еще и с привилегией seDebugPrivilege (Если поток зацикливается, то WaitFor будет ждать вечно)

Штатно - см. MBo © (26.08.03 07:40) [14], а еще лучше разбить функцию потока на небольшие промежутки и периодически проверять Terminated...


 
y-soft   (2003-08-26 08:29) [19]

Прохожий © (26.08.03 08:18) [17]
...Но сбрасываеться лишь основной поток, а тот - продолжает работать...

Это заблуждение - в приложениях, написанных на Delphi, первичный поток не завершается, пока не завершаться остальные потоки (см. код VCL)

...Поэтому мне и приходиться выгружать делфи, а потом снова открывать прокт. Програмно то его остановить - это не вопрос, а вот в процесе отладки...???

Попробуйте убить процесс через TaskManager - иногда получается, но может и завешать Delphi...:)


 
Camus   (2003-08-26 10:22) [20]

> Palladin © (25.08.03 16:05) [11]

> Ты не есть прав...
В чем же конкретно?

> TThreadSafe это тот же TList с TCriticalSection...
Азбука. Спасибо, я давно в курсе.
:)

> потоки можно пихать и в TList и в TObjectList...
Можно, если самому реализовать защиту от одновременного запихивания (или выпихивания) двух и более потоков (иначе последствия непредсказуемы). А список с такой защитой будет уже не любой - о чем и было сказано.

> термин потокозащищенность относится к обшепотоковым ресурсам а
> не к самим потокам...
Тоже азбука. Вот только список потоков и есть тот самый общепотоковый ресурс.
:)


 
Palladin   (2003-08-26 10:27) [21]


> Camus © (26.08.03 10:22) [20]

Список потоков не имеет ничего общего с содержащимися в них потокам, кроме разве того что он их содержит...


> Можно, если самому реализовать защиту от одновременного
> запихивания (или выпихивания) двух и более потоков (иначе
> последствия непредсказуемы). А список с такой защитой будет
> уже не любой - о чем и было сказано.

Зачем ее реализовывать? Объясни?

List:TList;

List.Add(TMyThread.Create);
List.Add(TMyThread.Create);

в TList содержатся не потоки а лишь указатели на объекты-оболочки потоковых функций... И потоки не заносят сами себя в TList. Этим занимается тот поток в контексте которого исполняется TMyThread.Create.


 
Sandman25   (2003-08-26 10:40) [22]

Palladin © (26.08.03 10:27)

Да, но удалять-то поток из списка потоков будет сам завершаемый поток (если без Synchronize, конечно).


 
Camus   (2003-08-26 10:57) [23]

> Palladin © (26.08.03 10:27) [21]
> Зачем ее реализовывать? Объясни?

Попробую. Навскидку могу назвать три причины.

1. Обработкой списка не обязательно занимается только один главный поток. Это зависит от того, как построена логика программы. Например, нам требуется список активных потоков (а не просто объектов) - тогда логично сделать, чтобы класс потока добавлял себя в список при входе в Execute (возможно, и в Resume), а удалял из него при выходе из Execute (возможно, и в Suspend).

2. Даже если потоки сами себя в список не добавляют и из него не удаляют, конфликт все равно возможен. Простой и логичный путь автоматического удаления - назначаем обработчик OnTerminate и в нем удаляем. Итак, завершился один поток, вызвался обработчик и выполнение ушло куда-то в недра кода TList. В это время завершается еще один поток, вызывается тот же обработчик и удаляется еще один элемент списка - в то время, как удаление первого уже началось, но еще не завершено. Кто как, а я за последствия я не ручаюсь.

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

Событийная модель - вот в чем все дело. Причем в случае с потоками эта событийность проявляется, наверное, даже отчетливее, чем, скажем, в случае с сообщениями (цикл выборки исполняется все же синхронно, а вот потоки - как хотят).


 
Camus   (2003-08-26 11:02) [24]

> Sandman25 (26.08.03 10:40) [22]

Даже если и с Synchronize, конфликт все равно не исключен. Потому что цепочка удаления N-го потока может быть запущена, когда цепочка удаления (N-1)-го уже началась, но еще не завершилась.


 
Sandman25   (2003-08-26 11:07) [25]

Camus

Вы уверены? Мне казалось, что при вызове Synchromize в очередь событий ставится запрос на выполнение процедуры основным потоком. И пока основной поток не закончит обработку предыдущего Synchronize, он не полезет в очередь событий и не начнет выполнять следующий Synchronize.
Аналогично тому, как при обработке нажатия одной кнопки основной поток не полезет в очередь обрабатывать нажатие другой кнопки, которую нажал пользователь в это время. Конечно, если нет явного вызова ProcessMessages. Я думаю, внутри Synchronize нет ProcessMessages.


 
Camus   (2003-08-26 11:17) [26]

> Sandman25 (26.08.03 11:07) [25]

> И пока основной поток не закончит обработку предыдущего
> Synchronize, он не полезет в очередь событий и не начнет
> выполнять следующий Synchronize.

Да, но только если в цепочке первого Synhronize юзер не написал ProcessMessages. А ему это раз плюнуть - например, он визуальный интерфейс обновить хочет.

Synhronize НЕ означает непременно последовательное исполнение. Он только гарантирует исполнение в контексте главного потока, вот и все. А событийность при этом не отменяется.


 
Palladin   (2003-08-26 11:22) [27]


> Camus © (26.08.03 10:57) [23]

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


 
Sandman25   (2003-08-26 11:30) [28]

Camus © (26.08.03 11:17)

Спасибо, буду знать.


 
Camus   (2003-08-26 11:44) [29]

> Palladin © (26.08.03 11:22) [27]
> Поток, содержащийся в List при завершении уведомляет менеджер
> потоков, через PostMessage или PostThreadMessage, что он
> завершился, после этого менеджер потоков сам удаляет экземпляр
> из List.

И стоит только (по невнимательности, или забывчивости, или три года спустя, или другому программисту, или... еще сотня "или") написать в цепочке удаления ProcessMessages, как тут же имеем реальный шанс получить тот же самый глюк. И вылавливать его причину будет ОЧЕНЬ тяжко. Потому что и глюк нестабильный, и причина неявная.


 
Palladin   (2003-08-26 12:35) [30]


> Camus © (26.08.03 11:44) [29]

Приведи код. У меня все прекрасно работает. Ну поставит он (кодер) processmessages, что дальше то? Где коллизия? Потокам безразлично. Как освобождающимся так и вновь созданным. Всем рулит менеджер. Он для того и создан. Объект потока не освобождает себя, не удаляет себя. Он лишь говорит менеджеру что он все. У менеджера очередь сообщений. Она нарушится не может. Что имеется в виду под "цепочка удаления"?


 
Camus   (2003-08-26 16:11) [31]

> Palladin © (26.08.03 12:35) [30]

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

Рассмотрим для примера вот что. В качестве менеджера потоков используем главную форму:

const
AM_THREADMESSAGE = WM_USER + 100;

type
TForm1 = class(TForm)
private
FThreadList: TList;
procedure AMThreadMessage(var Message: TMessage); message AM_THREADMESSAGE;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;

procedure TForm1.AMThreadMessage(var Message: TMessage);
begin
with Message, FThreadList do
if Boolean(LParam) then
FThreadList.Add(Pointer(WParam))
else
FThreadList.Remove(Pointer(WParam));
end;

constructor TForm1.Create(AOwner: TComponent);
begin
inherited;
FThreadList := TList.Create;
end;

destructor TForm1.Destroy;
begin
FThreadList.Free;
inherited;
end;

А сами потоки будут такими:

procedure TMyThread.Execute;
begin
FreeOnTerminate := True;
PostMessage(Application.MainForm.Handle, AM_THREADMESSAGE, Integer(Self), 1);
...
PostMessage(Application.MainForm.Handle, AM_THREADMESSAGE, Integer(Self), 0);
end;


Согласитесь, это и есть именно то, о чем Вы говорили.

Теперь пусть где-то (неважно где) эти потоки создаются. Само собой, дальше вся работа с потоками и их списком происходит автоматически. Завершился N-й поток, в очередь поступило сообщение, оно было выбрано и этот поток начал удаляться из списка. В это время завершается (N-1)-й поток и в очередь поступает еще одно сообщение. Оно пока еще не выбирается, так как главный поток выполняет удаление N-го потока. Предположим, при этом удалении уже было вызвано Remove, оттуда Delete, оттуда System.Move и в данный момент выполняется перемещение элементов (причем их количество уже запомнено в System.Move).

В этот момент откуда угодно (даже извне, например из хука или из другой программы) вызывается SendMessage с хэндлом любой формы или оконного контрола нашей программы. Соответственно, удаление N-го элемента приостанавливается, происходит прямой вызов оконной процедуры этой формы или контрола и возбуждается соответствующее событие, на которое повешен обработчик, а в нем написано Application.ProcessMessages. И будет вот что.

Выбирается сообщение от (N-1)-го потока и он удаляется из списка. Предположим лучший вариант, когда ситуация не повторяется и его удаление происходит нормально. Но вот потом выполнение кода пойдет обратно по стеку вызовов и возвратится в System.Move, выполняющую перемещение элементов для сообщения от N-го потока. А эта System.Move понятия не имеет о том, что список стал уже другим, что его счетчик изменился - она честно отработает с теми параметрами, которые ей были переданы при вызове. Что из этого получится? Все, что угодно - от ничего страшного до Access Violation (причем совсем не обязательно сразу, ошибка может проявиться и совершенно в другом месте).

Поэтому то, что у Вас все прекрасно работает означает лишь то, что у Вас подобная ситуация просто не возникала. Но нет никакой гарантии, что не возникнет завтра или через год.

Очередь сообщений нарушиться действительно не может. Но она определяет только порядок выборки сообщений. И этот порядок не означает, что более позднее сообщение не может быть обработано ДО завершения обработки более раннего. Пример выше.

И если рассмотреть аналогичный пример, но когда накладываются не два удаления, а удаление с добавлением (или наоборот), то может получиться вообще полная чехарда.


 
Прохожий   (2003-08-27 13:04) [32]

Ну и как же всё таки правильно это сделать??


 
panov   (2003-08-27 13:27) [33]

Все же довольно просто.
Я тебе в середине топика предлагал для этой простой задачи воспользоваться соответствующими функциями.

вот пример:

type
MyThread = class(TThread)
protected
procedure Execute;override;
end;

var

cnt: Integer;
...

procedure MyThread.Execute;
begin
FreeOnTerminate := True;
InterlockedIncrement(cnt);
Sleep(5000);
InterlockedDecrement(cnt);
end;


А обращение в основном потоке вот здесь:

procedure TForm1.Button2Click(Sender: TObject);
var
i: Integer;
begin
Timer1.Enabled := True;
for i := 0 to 1000 do
begin
MyThread.Create(False);
end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Memo1.Lines.Add(IntToStr(cnt));
if cnt=0 then Timer1.Enabled := False;
end;


 
panov   (2003-08-27 13:40) [34]

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


 
Camus   (2003-08-27 13:57) [35]

> Прохожий © (27.08.03 13:04) [32]

Например, так.

unit Unit2;

interface

uses
SysUtils, Classes;

type
TMyThread = class(TThread)
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: boolean);
destructor Destroy; override;
end;

const
MaxThreadNumber = 10;

function ThreadCount: integer;

implementation

var
ThreadList: TThreadList;

function ThreadCount: integer;
begin
with ThreadList, LockList do
try
Result := Count;
finally
UnlockList;
end;
end;

constructor TMyThread.Create(CreateSuspended: boolean);
begin
with ThreadList, LockList do
try
if Count = MaxThreadNumber then
raise Exception.CreateFmt("MaxThreadNumber = %d", [MaxThreadNumber]);
inherited;
FreeOnTerminate := True;
Add(Self);
finally
UnlockList;
end;
end;

destructor TMyThread.Destroy;
begin
ThreadList.Remove(Self);
inherited;
end;

procedure TMyThread.Execute;
begin
...
end;

initialization
ThreadList := TThreadList.Create;

finalization
ThreadList.Free;

end.


 
y-soft   (2003-08-27 15:08) [36]

>Camus © (27.08.03 13:57) [35]
if Count = MaxThreadNumber then
raise Exception.CreateFmt("MaxThreadNumber = %d", [MaxThreadNumber]);


А не лучше так:


...
const
MaxThreadNumber = 10;
var
Semaphore : THandle;
...
procedure TMyThread.Execute;
begin
if WaitForSingleObject(Semaphore, INFINITE) = WAIT_OBJECT_0 then
try
DoSomething;
finally
ReleaseSemaphore(Semaphore,1,nil);
end;
...
end;
...
initialization
Semaphore := CreateSemaphore(nil,MaxThreadNumber,MaxThreadNumber,nil);
finalization
CloseHandle(Semaphore);
end.


 
panov   (2003-08-27 15:37) [37]

Еще проще можно ограничить:

if InterlockedExchange(cnt,0)<MaxThreadNumber then MyThread.Create;

И списков не надо.


 
y-soft   (2003-08-27 15:54) [38]

panov © (27.08.03 15:37) [37]
if InterlockedExchange(cnt,0)<MaxThreadNumber then MyThread.Create;

Так в Win9X работать не будет, ну нет там такой функции:)

Потом - IMHO смысл ограничения количества потоков - это не ограничение общего количества потоков, а ограничение количества потоков ОДНОВРЕМЕННО РАБОТАЮЩИХ, чтобы не напрягать ресурсы системы. И пусть создается сколько угодно потоков и ждут себе спокойненько своей очереди. А если вдруг приложение начнет завершаться, то после уничтожения семафора все Wait-функции мгновенно вернут WAIT_ABANDONED и ожидающие потоки завершатся...


 
panov   (2003-08-27 16:23) [39]

>y-soft © (27.08.03 15:54) [38]

уточню все-таки-)
InterlockedExchange во всех версиях Win есть не ниже W95.

А вариантов - масса. Это один из многих-)


 
y-soft   (2003-08-27 18:32) [40]

>panov © (27.08.03 16:23) [39]
>>y-soft © (27.08.03 15:54) [38]

уточню все-таки-)
InterlockedExchange во всех версиях Win есть не ниже W95.


Устыдил:))



Страницы: 1 2 вся ветка

Форум: "Потрепаться";
Текущий архив: 2003.09.22;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.57 MB
Время: 0.013 c
1-71665
Immortal_Death
2003-09-08 14:06
2003.09.22
Иконка в прложениях без формы.


1-71719
Relaxxx
2003-09-10 15:23
2003.09.22
Красивый ПрогрессБар или Gauge


14-71882
Zhenka
2003-09-03 08:47
2003.09.22
Подскажите как оптимизировать код.


14-71889
Карлсон
2003-09-03 18:06
2003.09.22
книга про Windows для ну совсем чайников.


14-71866
aldapooh
2003-08-30 14:35
2003.09.22
С чего начать новичку





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский