Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 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.aspx

InterlockedIncrement
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.58 MB
Время: 0.046 c
4-1187171747
suspeded
2007-08-15 13:55
2008.04.20
Проверить права администратора


15-1204885877
Юрий Лосев
2008-03-07 13:31
2008.04.20
Найти количество нулевых бит в строке


15-1204929909
oxffff
2008-03-08 01:45
2008.04.20
Всех дам с 8 Марта поздравляем


15-1204819301
Пробегал...
2008-03-06 19:01
2008.04.20
Переход по CTRL + клик глючит


3-1195702264
Петро
2007-11-22 06:31
2008.04.20
Универсальный доступ к СУБД





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский