Форум: "Прочее";
Текущий архив: 2008.04.20;
Скачать: [xml.tar.bz2];
ВнизПодсчет количества вхождений потоков Найти похожие ветки
← →
Пробегал... (2008-03-03 01:07) [0]Задача такая - есть ядро програмы, оно потокобезопасное, соответственно функции ядра дергают различные потоки в разное время. Возник вопрос уничтожения ядра... Проблема в том, в каком-то потоке может начаться процесс уничтожения ядра, а в это время какой-то другой поток решит воспользоваться функциями ядра... ок, эту проблему я как решил - просто при начале уничтожения ядра выставляется соответствующий флаг, который не пускает никакие потоки в методы ядра, а точнее дает отлуп и все функции сразу возвращают false...
Но ведь возможна такая ситуация, что будет исполняться метод ядра какой-то (проверка уже прошла), и в это время начнется уничтожения ядра. Возможны всякие непредсказуемые коллизии.
Поэтому моя какая идея - надо подсчитывать количество вхождений различных потоков в методы ядра. Вошел поток в метод - на счетчике плюс 1, вышел из метода - минус один. Соответственно, при уничтожении ядра делаю так: запрещаю новые вхождения в методы ядра, после чего ожидаю когда счетчик опуститься в ноль - это означает, что все потоки отработали на ядре. Вроде бы логично.
Теперь вопрос как это реализовать. Самое простое - это обычный флаг типа boolean и счетчик типа integer. Вроде бы операции: inc / dec выполняются в один такт и тут коллизий не будет, но плохо что операции проверки флага и счетчика разделены во времени. Пример:function Core.SuperFunc: boolean;
begin
if FlagRun then
begin
inc(counter);
try
....
.....
finally
dec(counter);
end;
end
else
Result := false ;
end;
Хоть и маловероятна, но возможна какая ситуация. Один поток пройдет проверку FlagRun, но не успеет до inc(counter). А в это время произойдет уничтожение ядра, которое выставит сначала FlagRun а false, а потом проверит значение счетчика:FlagRun := false;
while counter > 0 then
Sleep(200);
Но понятно, что тот удачливый поток уже прошел проверку FlagRun, а значение счетчика еще не увеличил. Понятно, он может стать головной болью, хоть и очень маловероятной.
И как бы это все сделать в одно движение, по грамотному?
Смотрел на семафоры, вроде бы то что нужно, но кое-что непонятно. Как в одну операцию с семафорами реализовать вышепоказанный цикл? С одной стороны надо этим действием запретить новые вхождения потоков, а с другой стороны не помешать нормальному выхождению уже работающих потоков, чтобы отследить когда выйдет последний поток...
← →
Игорь Шевченко © (2008-03-03 01:10) [1]RTFM Interlocked functions
← →
Пробегал... (2008-03-03 01:19) [2]Игорь Шевченко © (03.03.08 1:10) [1]
хм... А разве inc / dec могут привести к конфликтам в многопоточном приложении? Я думал они в один такт выполняются и даже если несколько процессоров - конфликта не будет. Ну да ладно, как я понял это обыкновенные inc / dec, только обрамленные входом / выходом в критическую секцию, верно? Но проблему это не решает. ну будет так:function Core.SuperFunc: boolean;
begin
if FlagRun then
begin
InterlockedIncrement(counter);
try
....
.....
finally
InterlockedDecrement(counter);
end;
end
else
Result := false ;
end;
все равно проблема описанная остается. Ведь между проверкой флага и увеличением счетчика поток может потерять управление... хотя я туплю, придумал
← →
Zeqfreed © (2008-03-03 01:29) [3]Если даже особо мозги не напрягать, то можно делать безусловный Inc(Counter, Flag). Хотя, наверное, можно что-нибудь более красивое придумать :)
← →
Игорь Шевченко © (2008-03-03 01:32) [4]
> Ведь между проверкой флага и увеличением счетчика поток
> может потерять управление
RTFM Interlocked functions
← →
korneley © (2008-03-03 01:36) [5]Не нравятся семафоры - см. критические секции. (хотя по сути есть одно и тоже, имхо)
cs.enter;
if FlagRun then inc(counter);
cs.leave;
if counter > 0 then
try
...
finally
dec(counter);
end;
cs.enter;
FlagsRun := false;
cs.leave;
while counter > 0 do ...
Не проверялось, но суть в том, что изменение флага в одном месте и (проверка флага + действия по результатам проверки, в другом) в одной критической секции.
← →
Пробегал... (2008-03-03 01:36) [6]Видимо, так надо написать:
cs: TCriticalSection ;
...
function Core.SuperFunc: boolean;
begin
cs.Enter ;
try
if FlagRun then
inc(counter)
else
begin
Result := false ;
exit ;
end;
finally
cs.Leave ;
end;
try
....
.....
finally
cs.Enter ;
dec(counter);
cs.Leave ;
end;
end;
а соответственно при уничтожении ядра делать так:function Core.Destroy...
begin
FlagRun := false;
FlagClear := false ;
repeat
cs.Enter;
try
if counter < 1 then FlagClear := true ;
finally
cs.Leave ;
end;
if not FlagClear then Sleep(300) ;
until FlagClear ;
end;
покритикуйте? Осталось избавиться от sleep...
← →
Пробегал... (2008-03-03 01:40) [7]Игорь Шевченко © (03.03.08 1:32) [4]
RTFM Interlocked functions
может хватит уже? Я посмотрел, нету в дельфи такого раздела, в том числе и по Win32SDK. Нашел функции InterlockedIncrement / InterlockedDecrement. О чем и написал. Также написал почему мне это не совсем катит.
А если сложно написать что-то более вразумительное - то лучше вообще ничего не писать.
← →
Пробегал... (2008-03-03 01:43) [8]На MSDN кстати по этому слову тоже ищутся только функции:
http://msdn2.microsoft.com/en-us/library/aa450561.aspxInterlockedIncrement
InterlockedDecrement
InterlockedExchange
InterlockedTestExchange
InterlockedCompareExchange
InterlockedCompareExchangePointer
InterlockedExchangePointer
InterlockedExchangeAdd
я не понимаю как они мне могут помочь. Как альтернатива inc / dec понимаю. Но в комплексе проблему не решают.
Поэтому или кому-то надо уточнять, или кто-то даже не читал чуть моей проблемы. Спасибо.
← →
korneley © (2008-03-03 01:45) [9]не... при уничтожении, не проверку
counter
надо в секцию пихать, а изменениеFlagRun
, как минимум...
← →
Пробегал... (2008-03-03 01:52) [10]korneley © (03.03.08 1:45) [9]
при уничтожении, не проверку counter надо в секцию пихать, а изменение FlagRun, как минимум
а зачем?
← →
korneley © (2008-03-03 01:58) [11]Еще раз повторю: Изменение флага (FlagRun := ...) и проверку в другом потоке этого флага + инкремент счетчиков по результату проверки, это все, что надо засинхонизировать. Остальное приложится. Наверное :)))
← →
korneley © (2008-03-03 02:04) [12]
> а зачем?
А затем, что флаг изменится, а поток согласно ситуации в [0] успеет раньше его прочитать, т.к. изменение флага не в секции. И по барабану, что проверка в ней.
← →
Пробегал... (2008-03-03 02:05) [13]korneley © (03.03.08 1:58) [11]
Еще раз повторю: Изменение флага (FlagRun := ...) и проверку в другом потоке этого флага + инкремент счетчиков по результату проверки
я тоже могу повторить: зачем изменение флага в деструкторе вносить в критич. секцию? Приведи пример, когда код [6] сработает неправильно? Очень похоже на веру, типа вроде тебе так кажется подсознательно, поэтому так и надо ;)
← →
Пробегал... (2008-03-03 02:09) [14]korneley © (03.03.08 2:04) [12]
А затем, что флаг изменится, а поток согласно ситуации в [0] успеет раньше его прочитать, т.к. изменение флага не в секции
ну успеет, и что дальше? Он прочтет True и пойдет дальше, но к тому времени как можно заметить он уже будет в критической секции, а посему точно увеличит счетчик на единицу. А значит в деструкторе проверка в критической секции на counter не сработает и деструктор будет ждать пока поток не отработает.
← →
ЦУП © (2008-03-03 02:09) [15]Схематично:
function Core.SuperFunc: boolean;
begin
EnterCriticalSection(...);
if FlagRun then Inc(Counter);
LeaveCriticalSection(...);
Result := FlagRun;
if Result then
begin
try
....
.....
finally
InterLockedDecrement(Counter);
end;
end;
end;
← →
korneley © (2008-03-03 02:22) [16]
> Приведи пример, когда код [6] сработает неправильно?
Да не, скорее всего сработает:
Поток:if FlagRun
, получаетtrue
, деструкторFlagRun := false
, дальше... конечно в секцию не войдет, а когда войдет,counter
уже приплюсован... Ну ладно, будем считать это моими пристрастиями :)
← →
Пробегал... (2008-03-03 02:34) [17]ЦУП © (03.03.08 2:09) [15]
Схематично:
function Core.SuperFunc: boolean;
ну я вообще-то написал это уже в посте [6] ;) Разница только в том, что мое:cs.Enter ;
dec(counter);
cs.Leave ;
ты заменил на:InterLockedDecrement(Counter);
Самое главное я ступил и не догадался, что проверку на флаг и увеличение счетчика надо просто делать в критической секции ;)
← →
korneley © (2008-03-03 02:35) [18]Главное - обозначились пути решения [0] ;)
← →
ЦУП © (2008-03-03 02:39) [19]
> Пробегал... (03.03.08 02:34) [17]
С семафором будет значительно проще.
← →
Пробегал... (2008-03-03 02:43) [20]вопрос один - как в моем коде [6] избавиться от sleep?
По идее нужно использовать какую-нибудь WaitForXXX, но с каким-объектом? Мне нужна противоположность семафору. Семафор занят, когда набилось n-ое количество потоков/вхождений и свободен когда есть хотя бы еще одно "место". А мне нужно чтобы объект был занят, пока занято хотя бы одно место и освободился только когда счетчик вернулся в исходное положение...
← →
Пробегал... (2008-03-03 02:59) [21]ЦУП © (03.03.08 2:39) [19]
С семафором будет значительно проще.
я не представляю как семафор может мне помочь?
Допустим, при каждом вхождении в метод будет вызываться OpenSemaphore, а при выходе из метода CloseHandle, отлично.
Но как в деструкторе сделать остановку, пока счетчик не будет приведен в первоначальное состояние? Именно это нужно, именно когда счетчик вернется в первоначальное состояние - это значит все потоки "ушли". Но идеология семафора другая - он занят только когда счетчик в ноль... Я, наверное, могу сделать что-то типа:
function Core.Destroy...
var
Count: integer ;
begin
FlagRun := false ;
repeat
ReleaseSemaphore(h, 0, @count) ;
if Count <> cStartCount then
sleep(300) ;
until Count = cStartCount ;
end;
но во-первых, я не уверен, что тако финт ушами задокументирован:ReleaseSemaphore(h, 0, @count) ;
а по-другому я даже не знаю как узнать значение счетчика семафора без изменения его состояния.
И во-вторых, между:
FlagRun := false ;
и
ReleaseSemaphore(h, 0, @count) ;
пройдет время, за которое другой поток может уже обойти проверку флага, но семафор еще не затронуть. То есть, тоже надо все обрамлять в критич. секции. А это уже получается ничуть не проще варианта в [6].
И самое главное это не избавляет от sleep ;(
← →
Пробегал... (2008-03-03 03:03) [22]Пробегал... (03.03.08 2:59) [21]
Допустим, при каждом вхождении в метод будет вызываться OpenSemaphore, а при выходе из метода CloseHandle, отлично
точнее при каждом вхождении в метод будет вызываться WaitForSingleObject для уменьшения счетчика на 1, а при завершении метода вызываться ReleaseSemaphore для увеличения счетчика на 1.
При том, что при создании семафора заведомо будет указано большое lInitialCount, чтобы счетчик не ушел в ноль, тогда методы ядра заблокируются...
← →
Пробегал... (2008-03-03 04:11) [23]в общем, придумал только так:
cs := TCriticalSection.create ;
event := CreateEvent(nil, true, true, nil) ;
counter := 0 ;
Каждый метод оборачиваем:function Core.SomeFunc: boolean;
begin
cs.Enter ;
try
if FlagRun then
begin
inc(counter);
if counter = 1 then
ResetEvent(Event) ;
end
else
begin
Result := false ;
exit ;
end;
finally
cs.Leave ;
end;
try
РАБОТА МЕТОДА !!!
finally
cs.Enter ;
dec(counter);
if counter = 0 then
SetEvent(Event) ;
cs.Leave ;
end;
end;
а соответственно код деструктора такой:function Core.Destroy...
begin
cs.Enter;
try
FlagRun := false;
finally
cs.Leave ;
end;
WaitForSingleObject(Event, INFINITE) ;
CloseHandle(Event) ;
...
...
end;
Пожалуйста, критика? Скажите хоть что все верно и работать будет ;)
P.S. Какой же это геморой писать потокобезопасные классы... Прорва кода в каждом методе только лишь для безопасности, хотя сам метод может быть из одного вызова функции состоит ;)
← →
Игорь Шевченко © (2008-03-03 09:56) [24]Пробегал... (03.03.08 01:40) [7]
> А если сложно написать что-то более вразумительное - то
> лучше вообще ничего не писать.
http://ln.com.ua/~openxs/articles/smart-questions-ru.html
Читать наизусть до полного и окончательного просветления. Если не поможет - буду делать прививку от хамства.
← →
Пробегал... (2008-03-03 16:13) [25]Игорь Шевченко © (03.03.08 9:56) [24]
причем здесь хамство? Просто ваш уровень слишком высок для меня, он недостижим для меня сейчас и ваши ответы мне абсолютно бесполезны. Так в этом случае какой смысл это писать? Вы тратите время на написание ответа (пусть и две строчки), я трачу время чтобы хоть как-то понять ваш ответ и догадаться как он относится к моей проблеме.
В результате расточительная трата времени двух человек. Зачем?
Я просто подожду людей не таких профи, ответы которых мне возможно будут понятны. Вот и все. Не вижу проблем и вещей на которые можно обижаться.
С уважением.
← →
Пробегал... (2008-03-03 16:14) [26]Народ! Прокоменьте плиз мой пост [23] ? Вроде все верно, но как-то хочется взгляда со стороны ;)
← →
jack128_ (2008-03-04 11:11) [27]
> но как-то хочется взгляда со стороны ;)
со сторону - очень сложно и криво выглядит. Вот как на interlocked-функциях. Не тестил, но вроде должно работать
procedure DoWork;
begin
if Finish > 0 then
Exit;
InterlockedIncrement(ThreadCount);
try
// do work
finally
InterlockedDecrement(ThreadCount);
end;
end;
procedure Destroy;
begin
// выставляем флаг, чтобы не начинали работу новые потоки
InterlockedExchange(Finish, 1);
// Ждем, пока закончат работу уже запустившееся потоки
while InterlockedCompareExchange(ThreadCount, 0, 0) > 0 do
Sleep(0);
// Do destroy
end;
PS А Игоря ты зря не слушаешь.
← →
jack128_ (2008-03-04 11:15) [28]собственно я тоже что то перемудрил. наверно мона так:
procedure Destroy;
begin
// выставляем флаг, чтобы не начинали работу новые потоки
Finish := 1;
// Ждем, пока закончат работу уже запустившееся потоки
while ThreadCount > 0 do
Sleep(0);
// Do destroy
end;
← →
jack128_ (2008-03-04 11:39) [29]блин, нет, все не правельно. Но как то мона реализовать чисто на интерлоках, нутром чую :-)
← →
Пробегал... (2008-03-04 15:58) [30]jack128_ (04.03.08 11:39) [29]
блин, нет, все не правельно. Но как то мона реализовать чисто на интерлоках, нутром чую :-)
Жень, ну вот видишь. Если уж мастер дельфи не смог составить пример, исходя из советов ИШ"а, то на что мне рассчитывать, глупому новичку? ;)
jack128_ (04.03.08 11:11) [27]
А Игоря ты зря не слушаешь
да я слушаю, просто пользы нет. До сих пор пользы не нашел, а уж если еще и ты не смог сделать ;) Насчет сложности - согласен, немного дико выглядит, но я оформил вот так:class function TCore.ThreadSafeEnter: boolean;
begin
coreCriticalSection.Enter ;
try
if CoreFlagRun then
begin
inc(CoreCounterThread);
if CoreCounterThread = 1 then
ResetEvent(coreEvent) ;
Result := true ;
end
else
begin
Result := false ;
exit ;
end;
finally
coreCriticalSection.Leave ;
end;
end;
class procedure TCore.ThreadSafeLeave;
begin
coreCriticalSection.Enter ;
try
dec(CoreCounterThread);
if CoreCounterThread = 0 then
SetEvent(coreEvent) ;
finally
coreCriticalSection.Leave ;
end;
end;
А каждый метод ядра делает что-то вроде:Result := ThreadSafeEnter ;
if not Result then exit ;
try
РАБОТАЕМ
finally
ThreadSafeLeave
end;
Ну и в деструкторе просто выставляется флаг, после чего ожидается наступление события coreEvent, которое означает что последний поток "ушел" из методов ядра.
← →
Игорь Шевченко © (2008-03-04 16:29) [31]Пробегал... (04.03.08 15:58) [30]
> да я слушаю, просто пользы нет
Это бывает. Наверное тогда не стоит мучиться и форум мучить, а реализовать так, как в первых постах написано.
← →
jack128_ (2008-03-04 17:02) [32]
> Если уж мастер дельфи не смог составить пример,
потому что данный дельфи мастер сегодня туп как пробка ;-)
Это чистая алгоритмика, тут _знания_ никак не помогут. Собственно их и нет, я тока сегодня попробывал испольковать interlocked"ы.
Самый простой вариант - это пост [3], только проверку флага делать ВНУТРИ блока try - finally - end.
Правда в таком случае - я могу заставить ядро не уничтожаться никогда просто запуская новые потоки чаще, чем ядро проверяет значение счетчика потоков..
← →
jack128_ (2008-03-04 17:04) [33]
> это пост [3],
[2] естественно
← →
Пробегал... (2008-03-04 18:36) [34]jack128_ (04.03.08 17:02) [32]
ну в общем я все таки склоняюсь к своему конечному решению [30]. Не так просто, но зато вроде должно работать на ура. И при этом используются эвенты, что избавляет от циклов навроде:while not (проверка) do
sleep(XXX);
и не надо подбирать это XXX оптимальное и систему не загружает излишне ;)
← →
ага (2008-03-04 19:42) [35]Фигней страдаешь. Сначала нать завершить потоки, потом из майн валить ядро. Ну а так
var
CoreClients: integer;
CoreTerminate: boolean = false;
function SomeCoreFunction...
begin
Result:= false;
if CoreTerminate the Exit;
InterLockedIncrement(CoreClients);
try
...
finally
InterLockeddecrement(CoreClients);
end;
end;
procedure FinalizeCore
begin
CoreTerminate:= true;
while InterLockedCompareExcenge(CoreClients, 0, 0) <> 0 do Sleep(0);
DestroyCore();
end;
← →
ага (2008-03-04 19:45) [36]Не, так:
function SomeCoreFunction...
begin
Result:= false;
InterLockedIncrement(CoreClients);
try
if CoreTerminate the Exit;
...
finally
InterLockeddecrement(CoreClients);
end;
end
← →
Пробегал... (2008-03-04 20:15) [37]ага (04.03.08 19:42) [35]
while InterLockedCompareExcenge(CoreClients, 0, 0) <> 0 do Sleep(0);
на мой взгляд фиговый код.
← →
ага (2008-03-04 20:29) [38]
> Пробегал... (04.03.08 20:15) [37]
> ага (04.03.08 19:42) [35]
> while InterLockedCompareExcenge(CoreClients, 0, 0) <> 0
> do Sleep(0);
>
> на мой взгляд фиговый код.
Ню-ню. Сразу чуйствуется - специалист!
← →
ага (2008-03-04 20:44) [39]
> Сразу чуйствуется - специалист!
Ага. Особливо по [30]. Молодец, так и делай. Главное - ниче не меняй.
← →
ага (2008-03-04 20:51) [40]ЗЫ
> if CoreCounterThread = 0 then
Эт так, мелочь. По сравнинию с остальным.
Страницы: 1 2 вся ветка
Форум: "Прочее";
Текущий архив: 2008.04.20;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.045 c