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

Вниз

Vista и CriticalSection   Найти похожие ветки 

 
Добежал   (2008-10-22 21:17) [0]

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


type
 TForm1 = class(TForm)
   Button1: TButton;
   Memo1: TMemo;
   procedure FormCreate(Sender: TObject);
   procedure Button1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

 TTestThread = class(TThread)
 protected
   procedure Execute; override;
 end;

var
 Form1: TForm1;
 TestList: TThreadList ;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 TestList := TThreadList.Create ;
 TTestThread.Create(false);
end;

{ TTestThread }

procedure TTestThread.Execute;
begin
 while true do
 begin
   TestList.LockList ;
   sleep(200);
   TestList.UnlockList ;
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 Memo1.Lines.Add("Перед: "+IntToStr(GetTickCount));
 TestList.LockList ;
 Memo1.Lines.Add("После: "+IntToStr(GetTickCount));
 TestList.UnlockList ;
end;


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

При этом изредка другой поток тоже пытается войти в эту секцию. Оба они войти очевидно не могут, но я думал что если второй завис над входом, то он войдет после того как первый освободит эту секцию.

Но нифига! У меня в системе второй поток умудряется висеть по десятку секунд! В то время как первый поток за это время десятки раз покидает и заново входит в критическую секцию.
Конечно, это в общем можно объяснить, навроде второй поток засыпает, а первый поток как только выходит тут же входит опять, второй поток даже не успевает проснуться. Вроде логично, но ТАААКОЙ таймаут меня удивляет все таки. Я думал, коли поток уж несколько секунд ждет критич. секции, то другой поток не сможет в это время десятки раз входить и выходить из секции.

В связи с этим:

1) реально и нормально ли такое поведение в теории?

2) проверьте кто может на своей системе какие будут результаты? Розыч, например, слабо верит что такое является нормальным и у него на XP такой блокировки никогда не происходит. А у меня на Vista каждый раз! Вот мои результаты:

Перед: 31541686
После: 31549049

Перед: 31769135
После: 31786467


Видно, что хотя у активного потока sleep стоит всего 200 миллисекунд, но основной поток "пробивался" в первом случае 8 секунд (!!!), и во втором случае аж 17 секунд!!! за это время активный поток по идее где-то раз 90 вошел и вышел из этой критической секции.

Код теста выше.

Как тестировать - запускаете и жмете несколько раз на кнопку. Выкладываете результаты ;) Желательно с пояснением какая у вас ОС и железо.

У меня Vista home premium SP1 сборка 6001
Процессор Intel E4400 2ГГц

3) как бороться с такой фичей? Хотелось бы более менее "равного" распределения доступа к критической секции между потоками.


 
Eraser ©   (2008-10-22 21:21) [1]

не тестировал, но бороться можно добавив такой код
while true do
begin
  Sleep(0); // или 1. <<---------
  TestList.LockList ;
  sleep(200);
  TestList.UnlockList ;
end;


 
Добежал   (2008-10-22 21:57) [2]

И спасибо кстати всем тем, кто придумал эту гениальную защиту по борьбе с реферальными ссылками. Молодцы просто, красавцы даже!

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


 
oxffff ©   (2008-10-22 22:37) [3]


> Добежал   (22.10.08 21:17)


Процессор какой?


 
antonn ©   (2008-10-22 22:50) [4]


> oxffff ©   (22.10.08 22:37) [3]

скорее всего Intel E4400 2ГГц


 
oxffff ©   (2008-10-22 23:02) [5]


> скорее всего Intel E4400 2ГГц


Поведение очень странное, второму потоку "больше везет в захвате".
Возможно поможет

1. SetCriticalSectionSpinCount

2. Все же код выше "не совсем честный"

Вместо

procedure TTestThread.Execute;
begin
while true do
begin
  TestList.LockList ;
  sleep(200);            <- другой поток уже успевает отработать.
  TestList.UnlockList ;
end;
end;

Поставить

procedure TTestThread.Execute;
begin
while true do
begin
  TestList.LockList ;
  TestList.UnlockList ;
  sleep(200);
end;
end;


 
oxffff ©   (2008-10-22 23:06) [6]


> sleep(200);


Это слишком большое значение поскольку от самого освобождения до еще одного захвата проходит совсем ничего. Освободили, и тут же захватили.


 
oxffff ©   (2008-10-22 23:08) [7]


>  у него на XP такой блокировки никогда не происходит.


Здесь видимо дело в планировщике.


 
oxffff ©   (2008-10-22 23:25) [8]


> <- другой поток уже успевает отработать.


Не, это не правда. :)

Получается при TestList.UnlockList - т.е. при сигнализировании события в XP
есть запланированные ожидающие потоки, а в Vista ожидающие потоки не запланированы.


 
oxffff ©   (2008-10-22 23:34) [9]

Ну вот и ответ.

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=510229&SiteID=1

I was reading the article at http://msdn.microsoft.com/windowsvista/default.aspx?pull=/library/en-us/dnlong/html/AppComp.asp and noticed the section about critical sections.  There is a bullet point that says the following.

"Should prevent starvation. Applications that call Sleep while holding the critical section lock now can cause starvation for other threads that need the lock. Sleep calls should be placed after the LeaveCriticalSection call. "

Critical sections are no longer acquired in FIFO order, this since Windows2003 SP1. This means that on W2K3 and higher, in your sample, the same thread will stay running after leaving the critical section , while on XP or lower, the thread will pre-empt and the next waiting thread in FIFO order will get scheduled. This is done to prevent performnce degradation due to excessive context switching.


 
Добежал   (2008-10-23 11:58) [10]


> Это слишком большое значение поскольку от самого освобождения
> до еще одного захвата проходит совсем ничего. Освободили,
>  и тут же захватили.


так в том и дело - это повторение ситуации в проекте. Там тоже самое, один поток работает активно, но периодически освобождает критическую секцию, чтобы другие потоки если надо смогли ее захватить и поработать со списком. Поэтому он покидает секцию и тут же в нее входит.

В XP все было гуд, по очереди получали когда нужно и проблем не было. А вот в vista такие блин новвоведения... Ужасно, первый поток до 50 секунд может не получать доступа к критической секции ;(

SetCriticalSectionSpinCount вроде не работает на 9x... Не хотелось бы.

Может использовать для синхронизации мьютексы? В них то нет надеюсь таких усовершенствований...

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


 
guav ©   (2008-10-23 12:42) [11]

Ошибка в том, что есть необходимость в таком коде.
Искать по кейвордам "Synchronize" && "Архангельский" :)

А я буду иметь ввиду, что виндовая КС до Vista неэффективна.


 
guav ©   (2008-10-23 12:44) [12]

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


 
тимохов   (2008-10-23 12:49) [13]

2Автор. Спасибо за тему. Интересно.
Насколько я понимаю sleep(0) должен провоцировать смену активного потока. делать, действительно, нужно после покадания крит. секции.

Насчет мьютексов. Я думаю, тут опыт даст ответ. Попробуй. Расскажи нам. Лично мне оч. интересно.


 
тимохов   (2008-10-23 12:50) [14]

2gauv Что к алгоритму придираться. Причина то не в этом в любом случае. Причину Антонов Сергей вроде нашел.


 
guav ©   (2008-10-23 12:58) [15]

> [14] тимохов   (23.10.08 12:50)
> Причину Антонов Сергей вроде нашел.

За что ему кстати спасибо.

Получается, что КС до 2003 исправляла ошибки неправильного использования КС, при этом тормозила правильное.

Недавно пытался обсуждать на RSDN использование альтернативной КС (boost::mutex), тогда мнения разделились, хотя мне альтернативная КС показалась лучше (в т.ч. и по производительности). Эта ветка - ещё один аргумент против стандартной КС.


 
Добежал   (2008-10-23 13:23) [16]


> Ошибка в том, что есть необходимость в таком коде


о, да!
Я тоже так считаю, что все ошибки только от того, что приходится писать программы.


> Вопрос - что делается с TestList в каких потоках ?


ну представь что в списке хранится информация об устройствах. Один поток берет по очереди из списка информацию об устройствах и опрашивает их. И так по кругу пока его не остановят.

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

Само сканирование устройства сделано внутри критической секции, то есть поток выбирает устройство, сканирует его, и заносит информацию новую об устройстве обратно в список. Потом выходит из секции, заходит в нее заново, выбирает следующее устройство и процесс повторяется.

Сканирование устройства происходит внутри секции. Я пошел на это сознательно, ибо иначе придется выходить из секции, сканировать устройство, потом опять его искать в списке и заносить информацию. Устройств может быть относительно много, чтобы не тратить время на постоянный поиск устройства внутри списка (ведь во время сканирования устройства список устройств тогда может быть изменен).

Впрочем, я придумал как изменю ситуацию. Ведь можно просто запомнить позицию устройства в списке. Потом по входу в критич. секцию просто проверить тоже ли самое устройство находится на этом же месте, если да - то обновить. Будет работать с учетом того, что устройства редко добавляются / изменяются.

Но вообще для меня все это стало неожиданностью. Я знаю что сканирование не может занимать более 50мс, поэтому осознанно шел на то, думал в критич. секции потоки встают в очередь друг за другом, ибо при ожидании насколько я знаю для критической секции создается мьютекс.
Так говорит Рихтер, наверное в Vista решили прооптимизировать. Цель оптимизация понятна, но тем не менее ;)


 
guav ©   (2008-10-23 13:40) [17]

> [16] Добежал   (23.10.08 13:23)
> Я тоже так считаю, что все ошибки только от того, что приходится
> писать программы.

Не передёргивай. У тебя ошибка, неправильная синхронизация.


> Само сканирование устройства сделано внутри критической
> секции, то есть поток выбирает устройство, сканирует его,
> и заносит информацию новую об устройстве обратно в список.
> Потом выходит из секции, заходит в нее заново, выбирает
> следующее устройство и процесс повторяется.

Должно хватить этого
http://msdn.microsoft.com/en-us/library/ms686962(VS.85).aspx


 
guav ©   (2008-10-23 13:49) [18]

Когда КС используется правильно, на ней вообще блокировки редко происходят. Именно за счёт этого она и легче события, семафора или мьютекса.


 
Добежал   (2008-10-23 13:56) [19]


> У тебя ошибка, неправильная синхронизация


и в чем она неправильная?


> Должно хватить этого
> http://msdn.microsoft.com/en-us/library/ms686962(VS.85).
> aspx


Не должно. Я везде использую дельфовые TThreadList - мне так удобнее. Я рад, что MS начинает делать это на уровне системы, но хотя бы:

InterlockedPushEntrySList Function: Client Requires Windows Vista or Windows XP.
Даже в w2k не поддерживается.


> Когда КС используется правильно, на ней вообще блокировки
> редко происходят


совершенно верно. У меня все точно также. Поток работает себе и работает, изменение списка происходит крайне редко.. За день может и ни разу не наступить.

То есть, за целый день ни одной блокировки. Это можно считать редко?

Но зато когда она наступает эта блокировка - повиснуть все может нафиг секунд на 30. И это очень плохо.


 
Добежал   (2008-10-23 14:04) [20]


> Впрочем, я придумал как изменю ситуацию. Ведь можно просто
> запомнить позицию устройства в списке. Потом по входу в
> критич. секцию просто проверить тоже ли самое устройство
> находится на этом же месте, если да - то обновить


посмотрел код, вспомнил. Блин, фигово.

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

Поэтому при сканировании вне критической секции может произойти в теории следующее... Сканирование произведено, уведомления отосланы, далее поиск в списке сканируемого устройства, запись инфы о нем. Но вообще говорят, ответ на уведомление может придти раньше, чем будет заново заблокирован список активным потоком сканирующим. Поэтому сначала по реакции на уведомление будет изменено состояние устройства (а это более приоритетная операция, чем изменение свойств устройства во время сканирования), а потом активный сканирующий поток найдет устройство в списке и еще раз его изменит. А это уже приведет к проблемам и ошибкам... Вот и хз как делать теперь правильно ;(

Конечно, можно никакие уведомления не отсылать во время сканирования. Сначала просканировать, ЗАПОМНИТь какие уведомления надо отослать, потом зайти в критическую секцию, изменить данные устройства, выйти из секции, послать уже уведомление... Бррр.... код становится очень нечитаемым ;(((


 
guav ©   (2008-10-23 14:06) [21]

> [19] Добежал   (23.10.08 13:56)
> Не должно. Я везде использую дельфовые TThreadList - мне
> так удобнее.

Разница в том, что в виндовый Slist добавление или удаление элемента происходит вообще без блокировки.

Кажется, есть идея что тебе нужно. Своя КС у каждого устройства.


 
Добежал   (2008-10-23 14:14) [22]


> Своя КС у каждого устройства


да, сейчас обдумываю в том же направлении. Но много косяков. Рассмотрим тоже самое удаление:

Должен быть заблокирован список, потом выбрано устройство, заблокировано устройство... А если устройство в это время занято? В общем, боюсь взаимных блокировок, если сейчас все переделывать...


 
Добежал   (2008-10-23 14:15) [23]

Самое простое пока вот что придумал. Если неактивному потоку нужен доступ к секции, то где-нибудь с помощью InterlockedIncreament выставлять флаг. Если по выходу из секции активный поток видит что флаг выставлен - принудительно себе делает sleep... Костыль, но должно работать, хотя и не нравится мне это.


 
Добежал   (2008-10-23 14:16) [24]


> Разница в том, что в виндовый Slist добавление или удаление
> элемента происходит вообще без блокировки


я рад. Но неработа программы на windows 2000 категорически недопустима. Строго говоря, нужен и win 9x.


 
KSergey ©   (2008-10-23 14:18) [25]

> guav ©   (23.10.08 13:49) [18]
> Когда КС используется правильно, на ней вообще блокировки редко происходят.

Редко или ненадолго? Что тут имелось в виду?


 
guav ©   (2008-10-23 14:19) [26]

> [22] Добежал   (23.10.08 14:14)
> Должен быть заблокирован список, потом выбрано устройство,
> заблокировано устройство...

Можешь покопать в направлении поискать готовый код lock-free списка. http://www.google.com.ua/search?hl=ru&q=lock-free+list&meta=

An interlocked singly linked list (SList) eases the task of insertion and deletion from a linked list. SLists are implemented using a nonblocking algorithm to provide atomic synchronization, increase system performance, and avoid problems such as priority inversion and lock convoys.

SLists are straightforward to implement and use in 32-bit code. However, it is challenging to implement them in 64-bit code because the amount of data exchangeable by the native interlocked exchange primitives is not double the address size, as it is in 32-bit code. Therefore, SLists enable porting high-end scalable algorithms to Windows.


 
guav ©   (2008-10-23 14:20) [27]

> [25] KSergey ©   (23.10.08 14:18)

Я под блокировкой подразумевал захват несколькими потоками одновременно.
Завхат КС одним потоком - без блокировки. Захват вторым, когда первый держит - блокировка, которая должна быть редкостью.


 
Добежал   (2008-10-23 14:25) [28]


> Можешь покопать в направлении поискать готовый код lock-
> free списка


а смысл? Только в том, что insert делается без блокировки? нафиг мне это надо...


 
guav ©   (2008-10-23 14:35) [29]

> [28] Добежал   (23.10.08 14:25)
> нафиг мне это надо...

Ты ж в [22] боишься дедлоков,нет?


 
Добежал   (2008-10-23 14:41) [30]


> Ты ж в [22] боишься дедлоков,нет?


боюсь. Но вставка без лока не поможет все равно в этом деле.
Например, вставлять надо только если в списке нет устройства с таким же адресом, иначе выдавать отлуп.

Поэтому при вставке мне все равно надо заблокировать список и пробежаться по нему для поиска такого же адреса.

Короче сделал так, интересно насколько это приемлимо, выглядит как обход оптимизации windows ;)

InterlockedIncrement(iPlace);
Place.LockList;
try
...  работа...
finally
 Place.UnlockList;
 InterlockedDecrement(iPlace);
end;


А у активного потока есть что-то типа:

.....
finally
 Place.UnlockList;
 while iPlace > 0 then sleep(1);
end;


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


 
han_malign ©   (2008-10-23 14:55) [31]


> а это более приоритетная операция, чем изменение свойств
> устройства во время сканирования

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

Idle, Processing, Changed, Deleting

сканер:
Idle|Changed ==> Processing
Processing = update => Idle | Changed =?=> Idle | Deleting ==> <Null>

менеджер:
<Null> => Idle
Idle|Processing = change => Changed
Processing ==> Deleting | Idle ==> <Null>


 
Добежал   (2008-10-23 14:59) [32]


> - с какой радости она более приоритетная, если - до окончания
> сканирования и обновления - блокируется?


приоритетная не по времени, а по конечному результату.

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


 
guav ©   (2008-10-23 16:29) [33]

Что будет вообще лучше - "честная" очерёдность или вообще дать приоритет другому потоку ?


 
Добежал   (2008-10-23 17:32) [34]

не понял вопроса.


 
guav ©   (2008-10-23 19:02) [35]

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

Дать приоритет другому:
это:
while true do
begin
 TestList.LockList ;
 sleep(200);
 TestList.UnlockList ;
end;
ме будет выполняться, пока другому потоку нужен список


 
oxffff ©   (2008-10-23 19:22) [36]

Еще есть функция SwitchToThread.

while true do
begin
TestList.LockList ;
sleep(200);
TestList.UnlockList ;
 SwitchToThread;
end;


 
Добежал   (2008-10-27 10:57) [37]

SwitchToThread тоже не работает в win9x.


 
Cobalt ©   (2008-10-27 14:46) [38]

Автору: почитай статью:
DTF.RU - То, что вам никто не говорил о многозадачности в Windows  www.dtf.ru/articles/read.php?id=39888

Это к вопросу о том, почему "второй поток умудряется висеть по десятку секунд!"

Может быть натолкнет на какую мысль.


 
Добежал   (2008-10-27 18:18) [39]


> Это к вопросу о том, почему "второй поток умудряется висеть
> по десятку секунд!"
>
> Может быть натолкнет на какую мысль


Доброе утро.
На какую еще мысль? Причины, почему поток висит по десятку секунд я описал еще в самом начале ветки. Вопрос в другом - как добиться более равноправного доступа к критической секции.


 
Добежал   (2008-10-27 18:42) [40]

Впрочем, в результате размышлений я пришел к выводу, что не понимаю зачем в Vista сделали данную "оптимизацию".

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

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

Но ведь это немного бредово, это стимулирует использовать критические секции неправильно - в случаях, когда блокировка происходит часто и на нее тратится много процессорного времени. При правильном же использовании критических секций, как у меня допустим - когда блокировка происходит крайне редко, эта оптимизация только вредит. Нагрузку из-за блокировок она не снижает (ввиду редкого возникновения этих блокировок), а вот отзывчивость системы резко снижает, когда редко использующему критическую секцию потоку вдруг понадобился доступ - он может простоять в очереди и МИНУТУ!!! На мой взгляд это неправильно.

Так что уважаемый guav тут может задуматься тоже. Ибо я то как раз получается использую критические секции верно и от данной оптимизации мне только хуже. Становится очевидно, что данная оптимизация в Vista как раз предназначена сгладить последствия НЕПРАВИЛЬНОГО использования критических секций. А при правильном использовании только мешает ;(



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

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

Наверх





Память: 0.58 MB
Время: 0.01 c
15-1225230195
Германн
2008-10-29 00:43
2008.12.28
Спам-боты


15-1225271618
Jeer
2008-10-29 12:13
2008.12.28
Прощание с Магомаевым


6-1197227156
Алекс_
2007-12-09 22:05
2008.12.28
SPX/IPX


2-1226985517
Z-group
2008-11-18 08:18
2008.12.28
Delphi + Excel


15-1225427301
Gans
2008-10-31 07:28
2008.12.28
Соц.опрос (Какой зубной пастой вы пользуетесь?)





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