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

Вниз

На что заменить критические секции в Vista?   Найти похожие ветки 

 
Добежал   (2008-12-18 17:38) [0]

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

PTask = ^TTask;
TTask = record
 Action: TActionProcedure;
 Data: TData;
end;


Представим, что такого типа записи хранятся в некоем списке List.
Что делает поток? Из этого списка выбирает задания и основываясь на Action и Data что-то там делает. Потом выбирает следующее задание, выполняет его... И так далее, и так далее.

Чтобы добавить задание - нужно внести в этот список новую запись, ну понятно.

Остается вопрос синхронизации. Логично сделать этот список обычным TThreadList, ну или в целом синхронизировать с помощью критических секций. Но тут нас ожидает дикая засада в операционной системе Vista (заводил об этом тему здесь уже). Там изменили, "оптимизировали" критический секции, и так получается, что если поток активно работает со списком - то внешний поток, желающий добавить задание в этот список - может ждать невероятно долго. Рабочий поток может успеть СОТНИ раз (по времени это десятки секунд) войти и выйти в критическую секцию, синхронизирующую этот список, а внешний поток так и будет на ней висеть. То есть, в Vista полностью сняли принцип "первый в очереди - первый прошел" с критических секций (если такой принцип вообще был).

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

Вопрос - а что собственно делать для реализации сего действия? Сейчас я отказался от критических секций, сделал так сказать TThreadList, но основанный не на критичских секциях, а на Event"ах - там принцип "первый - первый" - видимо, пока поддерживается. Правильно ли я сделал? Какая гарантия, что в будущем MS отменит этот принцип и для Event"ов?

Как правильно реализовать задачу?


 
Пробегал2....   (2008-12-18 21:34) [1]

ау ;(


 
Игорь Шевченко ©   (2008-12-18 21:57) [2]


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


Тогда ему нужен всего лишь повышенный приоритет


 
Пробегал2....   (2008-12-18 22:15) [3]

Игорь Шевченко ©   (18.12.08 21:57) [2]

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

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


 
Пробегал2....   (2008-12-18 22:17) [4]

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

Я правильно понимаю или не так немножко все? А у каких объектов вообще какие принципы? У мьютексов как я заметил все осталось по-прежнему, первый застолбил - первый пройдешь. А как насчет семафоров, event"ов и прочего, интересно просто...


 
Игорь Шевченко ©   (2008-12-18 22:34) [5]


>  а вы вообще в теме насчет оптимизации крит. секций в Vista?


В теме.

"Starting with Windows Server 2003 with Service Pack 1 (SP1), threads waiting on a critical section do not acquire the critical section on a first-come, first-serve basis. This change increases performance significantly for most code. However, some applications depend on FIFO ordering and may perform poorly or not at all on current versions of Windows (for example, applications that have been using critical sections as a rate-limiter). To ensure that your code continues to work correctly, you may need to add an additional level of synchronization. For example, suppose you have a producer thread and a consumer thread that are using a critical section object to synchronize their work. Create two event objects, one for each thread to use to signal that it is ready for the other thread to proceed. The consumer thread will wait for the producer to signal its event before entering the critical section, and the producer thread will wait for the consumer thread to signal its event before entering the critical section. After each thread leaves the critical section, it signals its event to release the other thread.

Windows Server 2003 and Windows XP/2000:  Threads that are waiting on a critical section are added to a wait queue; they are woken and generally acquire the critical section in the order in which they were added to the queue. However, if threads are added to this queue at a fast enough rate, performance can be degraded because of the time it takes to awaken each waiting thread."


 
Добежал   (2008-12-19 11:07) [6]

то есть, для всех объектов ядра, предназначенных для синхронизации действует правило FIFO, кроме критических секций?


 
Riply ©   (2008-12-19 11:43) [7]

> [0] Добежал   (18.12.08 17:38)
> То есть, в Vista полностью сняли принцип "первый в очереди - первый прошел"
> с критических секций (если такой принцип вообще был).

> [4] Пробегал2....   (18.12.08 22:17)
> Я так понимаю, что раньше была логика кто первый "завис" над критической секцией
> - тот первый и войдет в нее после освобождения. А в Vista изменили принцип.

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

> [6] Добежал   (19.12.08 11:07)
> то есть, для всех объектов ядра, предназначенных для синхронизации действует правило FIFO,
> кроме критических секций?

Его (принципа) никогда и не было:
"Тут возникает интересный вопрос. Если несколько потоков ждет один объект ядра, какой из них пробудится при освобождении этого объекта? Официально Microsoft отвечает на этот вопрос так: «Алгоритм действует честно" Что это за алгоритм, Micro soft не говорит, потому что нс хочст связывать себя обязательствами всегда придер живаться именно этого алгоритма. Она утверждает лишь одно- если объект ожидает ся несколькими потоками, то всякий раз, когда этот объект переходит в свободное состояние, каждый из них получает шанс на пробуждение.

Таким образом, приоритет потока не имеет значения- поток с самым высоким приоритетом не обязательно первым захватит объект. Не получает преимущества и поток, который ждал дольше всех. Есть даже вероятность, что какой-то поток сумеет повторно захватить объект. Конечно, это было бы нечестно по отношению к другим потокам, и алгоритм пытается не допустить этого. Но никаких гарантий нет.

На самом деле этот алгоритм просто использует популярную схему "первым во шел — первым вышел" (FIFO). B принципе, объект захватывается потоком, ждавшим дольше всех. Но в системе могут произойти какие-то события, которые повлияют на окончательное решение, и ил-за этого алгоритм становится менее предсказуемым. Вот почему Microsoft и не хочет говорить, как именно он работает. Одно из таких собы тий — приостановка какого-либо потока. Если поток ждет объект и вдруг приоста навливается, система просто забывает, что он ждал этот объект. А причина в том, что нет смысла планировать приостановленный поток. Когда он в конце концов возоб новляется, система считает, что он только что начал ждать данный объект.

Учитывайте это при отладке, поскольку в точках прерывания (breakpoints) все потоки внутри отлаживаемого процесса приостанавливаются. Отладка делает алго ритм FIFO в высшей степени непредсказуемым из-за частых приостановки и возоб новления потоков процесса."
  (с) Рихтер


 
Добежал   (2008-12-19 11:47) [8]


> Его (принципа) никогда и не было:


потрясающий у тебя вывод, Riply. И далее по тексту ты сама же цитируешь:


> На самом деле этот алгоритм просто использует популярную
> схему "первым во шел — первым вышел" (FIFO).


 
Skyle ©   (2008-12-19 11:48) [9]


> Добежал   (19.12.08 11:47) [8]

А дальше ты читал?


 
Добежал   (2008-12-19 11:50) [10]

Но очевидно Рихтер все это писал до Vista. В ней алгоритм то как раз изменили, и принцип FIFO для критических секций более не действует. Riply, ты мне кажется не совсем въехала о чем мы говорим.


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


в Vista как раз алгоритм вполне допускает это. Я уже говорили несколько раз, можно добиться что некий поток перезахватывает критическую секцию более СОТНИ раз в то время, как второй поток все это время ждет входа.


 
Riply ©   (2008-12-19 11:50) [11]

> [8] Добежал   (19.12.08 11:47)
> потрясающий у тебя вывод, Riply. И далее по тексту ты сама же цитируешь:

А может сначала дочитать цитату, а потом удивляться моим выводам ? :)


 
Riply ©   (2008-12-19 11:53) [12]

> [10] Добежал   (19.12.08 11:50)
> Но очевидно Рихтер все это писал до Vista. В ней алгоритм то как раз изменили,
> и принцип FIFO для критических секций более не действует. Riply,
> ты мне кажется не совсем въехала о чем мы говорим.

Ripyl "въехала". И то что в Виста все осталось примерно так-же
доказывает твоя педыдущая ветка (на которую ты ссылаешся)  :)


 
KSergey ©   (2008-12-19 11:54) [13]

> Skyle ©   (19.12.08 11:48) [9]
> А дальше ты читал?

Я бы сказал - до :)


 
Добежал   (2008-12-19 11:54) [14]


> А дальше ты читал?


а ты читала? Рихтер прямым текстом говорит, что используется алгоритм FIFO. Естественно, с учетом других факторов. А ты говоришь, что там никакого FIFO не было никогда.

Вот в Vista FIFO для крит. секций отменили - это да, это факт. И это нагляднейше видно по работоспособности программы. На XP у меня интерфейс никогда не зависал, на Vista мог подвиснуть на минуту.


 
Skyle ©   (2008-12-19 11:55) [15]


> Добежал   (19.12.08 11:54) [14]

Ты промазал. Да, я читал.


> KSergey ©   (19.12.08 11:54) [13]

Ньюансы, оттенки смысла :)


 
Добежал   (2008-12-19 11:56) [16]


> И то что в Виста все осталось примерно так-же


надоело. Riply, прочитай хотя бы [5] а? Да, в висте все осталось по прежнему, именно поэтому microsoft пишет об изменениях.


 
Добежал   (2008-12-19 12:00) [17]

ну а точнее MS пишет об изменениях не в висте, а в NT линейке начиная с Windows Server 2003. Просто я столкнулся с этим именно на Vista. Алгоритм который НИКОГДА не приводил к зависаниям интерфейса хотя бы на секунду, в Vista стандартно вешался на минуту и более.

Да, да, в vista ничего не изменили. Именно об этом и пишет MS, что начиная с Windows Server 2003 они ничего не меняли. Там на тарабарском языке именно так и написано.


 
Тын-Дын ©   (2008-12-19 13:28) [18]


> Да, да, в vista ничего не изменили. Именно об этом и пишет
> MS, что начиная с Windows Server 2003 они ничего не меняли.
>  Там на тарабарском языке именно так и написано.


Почему я не заметил отличий?
У меня одни и те же программы работают как на XP, так и на W2003, и на Vista.


 
Игорь Шевченко ©   (2008-12-19 14:04) [19]

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

Что происходит с критическими секциями, написано в [5] - это цитата из MSDN


 
Городской Шаман   (2008-12-19 14:10) [20]


> Добежал   (18.12.08 17:38)  


А Sleep(0) между итерациями цикла не помогает?


 
Добежал   (2008-12-19 14:18) [21]


> А Sleep(0) между итерациями цикла не помогает?


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


 
oxffff ©   (2008-12-19 14:22) [22]

??

//by oxffff aka Sergey Antonov
TAbstractLOCK=class
public
function TRYLOCK:integer;virtual;abstract;
procedure LOCK;virtual;abstract;
procedure UNLOCK;virtual;abstract;
end;

TSPINLOCK=class(TAbstractLOCK)
protected
lSynchronizeInt:integer;
public
constructor create;
function TRYLOCK:integer;override;
procedure LOCK;override;
procedure UNLOCK;override;
end;

constructor TSPINLOCK.create;
begin
lSynchronizeInt:=RESOURCE_FREE;
end;

function TSPINLOCK.TRYLOCK:integer;
begin
case InterlockedExchange(lSynchronizeInt,RESOURCE_LOCKED) of
RESOURCE_FREE:result:=CONST_OK;
else result:=CONST_NOT;
end;
end;

procedure TSPINLOCK.LOCK;
begin
while InterlockedExchange(lSynchronizeInt,RESOURCE_LOCKED)=RESOURCE_LOCKED do;
end;

procedure TSPINLOCK.UNLOCK;
begin
InterlockedExchange(lSynchronizeInt,RESOURCE_FREE);
end;

{ TSmartObject }


 
oxffff ©   (2008-12-19 14:23) [23]

CONST
      RESOURCE_FREE=0;
      RESOURCE_LOCKED=1;


 
Добежал   (2008-12-19 14:24) [24]

попытаюсь объяснить, почему так считаю. Ведь Sleep(x) это комманда, которая говорит диспетчеру потоков, что потоку не нужно процесорное время еще x миллисекунд. Все пользуются тем, что sleep(0) приводит к переключению контекста, но ведь это я думаю нигде не запротоколировано. Поэтому не совсем верно так писать.

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


 
oxffff ©   (2008-12-19 14:25) [25]

Еще есть же Interlocked Singly Linked Lists с тем же механизмом.


 
oxffff ©   (2008-12-19 14:27) [26]


> Добежал   (19.12.08 14:24) [24]


Используй spinlock так называют его в режиме ядра.
Но смысл крутиться в цикле с постоянным опросом.


 
Добежал   (2008-12-19 14:37) [27]


> oxffff


да, думал о таком. Но алгоритм думаю нужно доработать так, как доработали критические секции в w2k. То есть что-то типа:

TAbstractLOCK=class
public
 FCountRepeat: integer;
 FSyncEvent: THandle; // на случай долгого ожидания синхронизирующий эвент


...

procedure TSPINLOCK.LOCK;
var
 iCount: integer;
 bLocked: boolean;
begin
 iCount := 0;
 bLocked := false;
while InterlockedExchange(lSynchronizeInt,RESOURCE_LOCKED) =RESOURCE_LOCKED do
 begin
   inc(iCount);
   if iCount > FCountRepeat then
   begin
     WaitForSingleObject(FSyncEvent, INFINITE);
     bLocked := true;
   end
   else Sleep(0);
 end;
 if not bLocked then WaitForSingleObject(FSyncEvent, INFINITE);
end;


то есть, такой симбиоз. И в каждом конкретном случае надо просто грамотно выбрать FCountRepeat


 
Добежал   (2008-12-19 14:41) [28]

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


 
oxffff ©   (2008-12-19 14:45) [29]


> Добежал   (19.12.08 14:37) [27]


Смысл spinlock как раз состоит в том, что ожидание предполагается, что ожидание минимально.
А работа со списком как раз это предполагает.

Я думаю проверка лишняя при очень быстрых доступах.


>    inc(iCount);
>    if iCount > FCountRepeat then
>    begin
>      WaitForSingleObject(FSyncEvent, INFINITE);
>      bLocked := true;
>    end
>    else Sleep(0);
>  end;
>  if not bLocked then WaitForSingleObject(FSyncEvent, INFINITE);
>


 
Игорь Шевченко ©   (2008-12-19 14:45) [30]


> Ведь Sleep(x) это комманда, которая говорит диспетчеру потоков,
>  что потоку не нужно процесорное время еще x миллисекунд.
>


Sleep приводит к ожиданию потока на объекте ядра "таймер". В случае интервала таймера равного нулю, таймер просто срабатывает мгновенно, но срабатывает весь механизм диспетчеризации потоков. Поэтому после sleep(0) поток может продолжать выполняться, как ни в чем не бывало, без переключения на другие потоки


 
oxffff ©   (2008-12-19 14:47) [31]


> Добежал   (19.12.08 14:41) [28]


Есть даже такая функция.
InitializeCriticalSectionAndSpinCount


 
Добежал   (2008-12-19 14:55) [32]


> InitializeCriticalSectionAndSpinCount


верно, я про нее и говорю. Но появилась она только в w2k.


> Я думаю проверка лишняя при очень быстрых доступах


ну при очень быстрых доступах до ухода в режим ядра и не дойдет. А уж проверка "if a > b" можно считать вообще не отнимает процессорное время по сравнению с остальным.

Впрочем, данная реализация не очень хороша...


> Sleep приводит к ожиданию потока на объекте ядра "таймер".
>  


хм.. Логично... То есть, функция Sleep приводит к переключению в режим ядра со всеми вытекающими процессорными затратами, верно?


 
oxffff ©   (2008-12-19 14:57) [33]


> Добежал   (19.12.08 14:55) [32]


У тебя работа связана с андронным коллайдером?
Думаешь он поэтому не работает?
:)


 
KSergey ©   (2008-12-19 15:03) [34]

> Skyle ©   (19.12.08 11:55) [15]
> Ньюансы, оттенки смысла :)

Я имел ввиду не "дочитал", а то, что до указанного места говорится как оно по документации, а потом - как реализовали это в MS.
Беда в том, что и я при чтении этого места всегда описанную реализацию принимал за аксиому, и лишь Рипли - спасибо ей - надоумила своей цитатой меня на то, что далее второго из процитированных абзацев читать и вовсе не стоит. Лишь для расширения кругозора...


 
Добежал   (2008-12-19 15:03) [35]

не :)
Я пытаюсь сделать "идеальную" реализацию TThreadList. Сделать ее и забыть, всегда пользуясь шаблоном. До недавних пор я пользовался самим TThreadList и был доволен, но в Vista напоролся на безумство в виде того, что один поток сотни раз входит и выходит из критической секции, в то время как другой поток минуту висит над этой критической секцией.
Вот думаю...


> При диспетчеризации потоков, ожидающих освобождения объектов
> синхронизации ядра происходит обслуживание по принципу очередей
> с приоритетами


Игорь, вы уверены? У меня есть ощущение, что работает все таки принцип FIFO, то есть если уж поток первым добрался до Wait функции, то он первым и "проскочит". Окромя исключения в виде критический секций, начиная с w2003s


 
Игорь Шевченко ©   (2008-12-19 15:04) [36]


> Игорь, вы уверены?


Да


 
Игорь Шевченко ©   (2008-12-19 15:04) [37]

Удалено модератором
Примечание: Дубль


 
KSergey ©   (2008-12-19 15:06) [38]

> Добежал   (19.12.08 14:55) [32]
>
> > InitializeCriticalSectionAndSpinCount
>
>
> верно, я про нее и говорю. Но появилась она только в w2k.
>

Вот. Это ка краз говорит о том, что есть большой смысл сделать 2 разных реализации: для 9x/NT4 и 2000 и далее. Разруливать в ран-тайм созданием объекта правильного для текущей ОС класса.


 
Riply ©   (2008-12-19 16:57) [39]

Это код:

var
GlobalThredID: ULONG = 0;
GlobalCount: integer = 0;

type
PTHREAD_CS = ^THREAD_CS;
THREAD_CS = packed record
 pCS: PRTL_CRITICAL_SECTION;
 Iterations: integer;
 ThreadID: THANDLE;
 aCount: integer;
 _Stuff: ULONG;
end;

THREADS_ARRAY = array of THREAD_CS;

function ThreadFunction(pThreadCS: Pointer): integer;
var
i: integer;
begin
Result := 0;
for i := 0 to Pred(PTHREAD_CS(pThreadCS).Iterations) do
 begin
  RtlEnterCriticalSection(PTHREAD_CS(pThreadCS).pCS);
  try
   if i = 0 then
    begin
     inc(GlobalCount);
     Log_WriteMessageNt("Start", "Step: " + IntToStr(i) + "   ID: " + IntToStr(GlobalThredID) + "   ActiveThreads: " + IntToStr(GlobalCount), STATUS_SUCCESS);
    end;

   if GlobalCount = PTHREAD_CS(pThreadCS).aCount then
    if GlobalThredID = PTHREAD_CS(pThreadCS).ThreadID
     then Log_WriteMessageNt("Break FIFO", "Step: " + IntToStr(i) + "   ID: " + IntToStr(GlobalThredID) + "   ActiveThreads: " + IntToStr(GlobalCount), STATUS_SUCCESS);

   GlobalThredID := PTHREAD_CS(pThreadCS).ThreadID;

   if i = Pred(PTHREAD_CS(pThreadCS).Iterations) then
    begin
     Log_WriteMessageNt("Stop", "Step: " + IntToStr(i) + "   ID: " + IntToStr(GlobalThredID) + "   ActiveThreads: " + IntToStr(GlobalCount), STATUS_SUCCESS);
     Dec(GlobalCount);
    end;
  finally
   RtlLeaveCriticalSection(PTHREAD_CS(pThreadCS).pCS);
  end;
 end;
end;

procedure TMainForm.btnTest_2Click(Sender: TObject);
var
ThreadCS: RTL_CRITICAL_SECTION;
i, ThreadsCount, IterationCount: integer;
RetStatus: NTSTATUS;
ThreadID: ULONG;
ParamsArr: THREADS_ARRAY;
ThreadsArr: array of THANDLE;
begin
inherited;
RetStatus := RtlInitializeCriticalSection(@ThreadCS);
if NT_SUCCESS(RetStatus) then
 try
  ThreadsCount := 3;
  IterationCount := 10000;
  SetLength(ThreadsArr, ThreadsCount);
  SetLength(ParamsArr, ThreadsCount);
  try
   for i := 0 to Pred(ThreadsCount) do
    with ParamsArr[i] do
     begin
      pCS := @ThreadCS;
      Iterations := IterationCount;
      aCount := ThreadsCount;
      ThreadsArr[i] := BeginThread(nil, 0, ThreadFunction, @ParamsArr[i], CREATE_SUSPENDED, ThreadID);
     end;

   for i := 0 to Pred(ThreadsCount) do ResumeThread(ThreadsArr[i]);

   WaitForMultipleObjects(ThreadsCount, @ThreadsArr[0], True, INFINITE);

  finally
   for i := 0 to Pred(ThreadsCount) do CloseHandle(ThreadsArr[i]);
  end;
 finally
  RtlDeleteCriticalSection(@ThreadCS);
 end;
MessBox_Inf("End of work");
end;


 
Riply ©   (2008-12-19 17:01) [40]

Это лог:
0000003628 19.12  16:44:27.640 Start Step: 0   ID: 0   ActiveThreads: 1 The operation completed successfully 0000000001 0000000863
0000003864 19.12  16:44:27.640 Start Step: 0   ID: 3628   ActiveThreads: 2 The operation completed successfully 0000000002 0000000864
0000003952 19.12  16:44:27.640 Start Step: 0   ID: 3628   ActiveThreads: 3 The operation completed successfully 0000000003 0000000865
0000003864 19.12  16:44:27.656 Break FIFO Step: 355   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000004 0000000866
0000003952 19.12  16:44:27.671 Break FIFO Step: 1106   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000005 0000000867
0000003952 19.12  16:44:27.671 Break FIFO Step: 1107   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000006 0000000868
0000003628 19.12  16:44:27.671 Break FIFO Step: 2070   ID: 3628   ActiveThreads: 3 The operation completed successfully 0000000007 0000000869
0000003864 19.12  16:44:27.671 Break FIFO Step: 1959   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000008 0000000870
0000003864 19.12  16:44:27.671 Break FIFO Step: 1960   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000009 0000000871
0000003864 19.12  16:44:27.671 Break FIFO Step: 1961   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000010 0000000872
0000003864 19.12  16:44:27.671 Break FIFO Step: 1962   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000011 0000000873
0000003864 19.12  16:44:27.671 Break FIFO Step: 1963   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000012 0000000874
0000003864 19.12  16:44:27.671 Break FIFO Step: 1964   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000013 0000000875
0000003864 19.12  16:44:27.671 Break FIFO Step: 1965   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000014 0000000876
0000003864 19.12  16:44:27.671 Break FIFO Step: 1966   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000015 0000000877
0000003864 19.12  16:44:27.671 Break FIFO Step: 1967   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000016 0000000878
0000003864 19.12  16:44:27.671 Break FIFO Step: 1968   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000017 0000000879
0000003864 19.12  16:44:27.671 Break FIFO Step: 1969   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000018 0000000880
0000003864 19.12  16:44:27.671 Break FIFO Step: 1970   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000019 0000000881
0000003864 19.12  16:44:27.671 Break FIFO Step: 1971   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000020 0000000882
0000003864 19.12  16:44:27.671 Break FIFO Step: 1972   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000021 0000000883
0000003864 19.12  16:44:27.671 Break FIFO Step: 1973   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000022 0000000884
0000003864 19.12  16:44:27.671 Break FIFO Step: 1974   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000023 0000000885
0000003864 19.12  16:44:27.671 Break FIFO Step: 1975   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000024 0000000886
0000003864 19.12  16:44:27.671 Break FIFO Step: 1976   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000025 0000000887
0000003864 19.12  16:44:27.671 Break FIFO Step: 1977   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000026 0000000888
0000003628 19.12  16:44:27.703 Break FIFO Step: 3599   ID: 3628   ActiveThreads: 3 The operation completed successfully 0000000027 0000000889
0000003628 19.12  16:44:27.703 Break FIFO Step: 3600   ID: 3628   ActiveThreads: 3 The operation completed successfully 0000000028 0000000890
0000003628 19.12  16:44:27.703 Break FIFO Step: 3601   ID: 3628   ActiveThreads: 3 The operation completed successfully 0000000029 0000000891
0000003628 19.12  16:44:27.703 Break FIFO Step: 3602   ID: 3628   ActiveThreads: 3 The operation completed successfully 0000000030 0000000892
0000003628 19.12  16:44:27.703 Break FIFO Step: 3603   ID: 3628   ActiveThreads: 3 The operation completed successfully 0000000031 0000000893
0000003864 19.12  16:44:27.703 Break FIFO Step: 3638   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000032 0000000894
0000003952 19.12  16:44:27.718 Break FIFO Step: 4495   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000033 0000000895
0000003952 19.12  16:44:27.718 Break FIFO Step: 4497   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000034 0000000896
0000003952 19.12  16:44:27.718 Break FIFO Step: 4498   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000035 0000000897
0000003952 19.12  16:44:27.718 Break FIFO Step: 4499   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000036 0000000898
0000003952 19.12  16:44:27.718 Break FIFO Step: 4500   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000037 0000000899
0000003952 19.12  16:44:27.718 Break FIFO Step: 4501   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000038 0000000900
0000003952 19.12  16:44:27.718 Break FIFO Step: 4502   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000039 0000000901
0000003952 19.12  16:44:27.718 Break FIFO Step: 4503   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000040 0000000902
0000003952 19.12  16:44:27.718 Break FIFO Step: 4504   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000041 0000000903
0000003952 19.12  16:44:27.718 Break FIFO Step: 4505   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000042 0000000904
0000003952 19.12  16:44:27.718 Break FIFO Step: 4506   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000043 0000000905
0000003952 19.12  16:44:27.718 Break FIFO Step: 4507   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000044 0000000906
0000003864 19.12  16:44:27.718 Break FIFO Step: 4778   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000045 0000000907
0000003864 19.12  16:44:27.718 Break FIFO Step: 4779   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000046 0000000908
0000003864 19.12  16:44:27.734 Break FIFO Step: 5168   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000047 0000000909
0000003864 19.12  16:44:27.734 Break FIFO Step: 5169   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000048 0000000910
0000003952 19.12  16:44:27.765 Break FIFO Step: 7894   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000049 0000000911
0000003864 19.12  16:44:27.796 Break FIFO Step: 9287   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000050 0000000912
0000003864 19.12  16:44:27.796 Break


 
Riply ©   (2008-12-19 17:06) [41]

Не уместился. Вот его кончик:
0000003864 19.12  16:44:27.734 Break FIFO Step: 5169   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000048 0000000910
0000003952 19.12  16:44:27.765 Break FIFO Step: 7894   ID: 3952   ActiveThreads: 3 The operation completed successfully 0000000049 0000000911
0000003864 19.12  16:44:27.796 Break FIFO Step: 9287   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000050 0000000912
0000003864 19.12  16:44:27.796 Break FIFO Step: 9288   ID: 3864   ActiveThreads: 3 The operation completed successfully 0000000051 0000000913
0000003628 19.12  16:44:27.796 Stop Step: 9999   ID: 3628   ActiveThreads: 3 The operation completed successfully 0000000052 0000000914
0000003864 19.12  16:44:27.796 Stop Step: 9999   ID: 3864   ActiveThreads: 2 The operation completed successfully 0000000053 0000000915
0000003952 19.12  16:44:27.796 Stop Step: 9999   ID: 3952   ActiveThreads: 1 The operation completed successfully 0000000054 0000000916

И немного пояснений:
С трудом удалось подобрать маленький лог.
Обычно (при этих параметрах) было, примерно, 200 - 300 записей (нить повторно захватывала объект).
Бывало, что и ни одной. Это когда не закрывая приложение, прогоняем тест раз десять.


 
Добежал   (2008-12-19 17:39) [42]

это к чему все?


 
Riply ©   (2008-12-19 17:53) [43]

> [42] Добежал   (19.12.08 17:39)
> это к чему все?

К тому, что мне пришлось выбирать между такими вариантами:
1. Ты не умеешь читать.
2. Ты умеешь читать, но не умеешь понимать что написано.
3. Ты настоль закомплексован, что не можешь призать свою ошибку
  и будешь стоять на своем до последнего (часто подтасовывая факты).
4. Ты просто еще до конца не разобрался.

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


 
Городской Шаман   (2008-12-19 18:28) [44]


> Добежал   (19.12.08 14:24) [24]
>
> попытаюсь объяснить, почему так считаю. Ведь Sleep(x) это
> комманда, которая говорит диспетчеру потоков, что потоку
> не нужно процесорное время еще x миллисекунд. Все пользуются
> тем, что sleep(0) приводит к переключению контекста, но
> ведь это я думаю нигде не запротоколировано. Поэтому не
> совсем верно так писать.


Как я делал. У меня была очередь пакетов на обработку, которые ставили в эту очередь другие потоки. Рабочий поток обрабатывающий очередь при "всплывании" сперва вычитывал и обрабатывал все пакеты без sleep(0) как только при очередной итерации count =0 то в теле итерации происходило переключение не на блок обработки а на блок sleep(0);

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


 
Добежал   (2008-12-22 11:38) [45]


> переключение не на блок обработки а на блок sleep(0);


Игорь Шевченко ясно же показал, что sleep(0) вовсе не обязательно должно переключить поток. В принципе, поток после этого может продолжать выполняться.

В принципе, это и логично. Sleep - комманда, которая говорит потоку "зависнутЬ" на такое-то время. Это особенность, что при sleep(0) обычно происходит переключение потоков. Но это не задокументировано и делать так в общем-то неправильно (полагаться на это).


 
Городской Шаман   (2008-12-22 14:20) [46]


> Добежал   (22.12.08 11:38) [45]
> В принципе, это и логично. Sleep - комманда, которая говорит
> потоку "зависнутЬ" на такое-то время. Это особенность, что
> при sleep(0) обычно происходит переключение потоков. Но
> это не задокументировано и делать так в общем-то неправильно
> (полагаться на это).


A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution.
http://msdn.microsoft.com/en-us/library/ms686298(VS.85).aspx


 
Добежал   (2008-12-22 16:02) [47]

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

Sleep приводит к ожиданию потока на объекте ядра "таймер". В случае интервала таймера равного нулю, таймер просто срабатывает мгновенно, но срабатывает весь механизм диспетчеризации потоков. Поэтому после sleep(0) поток может продолжать выполняться, как ни в чем не бывало, без переключения на другие потоки

а вот судя по MSDN, он НЕ может продолжать выполняться как ни в чем не бывало, если есть другие потоки равного приоритета.


 
Городской Шаман   (2008-12-22 18:01) [48]


> Добежал   (22.12.08 16:02) [47]
> а вот судя по MSDN, он НЕ может продолжать выполняться как
> ни в чем не бывало, если есть другие потоки равного приоритета.


и они готовы к выполнению.


 
Добежал   (2008-12-22 18:08) [49]

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


 
Игорь Шевченко ©   (2008-12-22 18:17) [50]

Добежал   (22.12.08 16:02) [47]


> If there are no other threads of equal priority ready to
> run, the function returns immediately, and the thread continues
> execution.


Какое именно слово из процитированных противоречит моему посту ?


> Поэтому после sleep(0) поток может продолжать выполняться,
>  как ни в чем не бывало, без переключения на другие потоки


?


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

ну вероятно я вас неправильно понял


 
vuk ©   (2008-12-23 13:06) [52]

Я в случаях, когда нужно обработка типа поставщики-потребитель и у заданий равный приоритет, я делаю так:

1. Есть потребитель, который большую часть времени тихо дрыхнет, отслушивая, как правило, пару Event-ов. Один event говорит, что есть данные на обработку, а второй - что пришла хана, надо сворачиваться и валить.

2. Есть буфер, в который поставщики складывают задания, по складыванию задания поднимается event, по которому просыпается потребитель. Доступ к буферу блокируется через CriticalSection.

3. По event-у о наличии данных потребитель просыпается, забирает всё, что есть в буфере в свой внутренний буфер и тихо там жует, не мешая поставщикам сваливать задания в общий буфер.

4. После того, как потребитель прожевал то, что ухватил, он идет спать.


 
Добежал   (2008-12-23 15:02) [53]

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

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

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


 
vuk ©   (2008-12-23 15:29) [54]

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


 
Eraser ©   (2008-12-23 17:12) [55]

> [52] vuk ©   (23.12.08 13:06)

классическая схема, оправдывает себя на 100%.
есть еще вариант с семафором, т.е.

1 поток (потребитель):

wait(sm1);
enter(cs);
// <<-- извлечение данных из буффера.
leave(cs);
release(sm2);

2 поток (обработчик (генератор) данных):
wait(sm2);
enter(cs);
// <<-- помещаем данные в буффер.
leave(cs);
release(sm1);

у семафоров MaximumCount = 1.
эта схема оправдана, когда работа двух потоков должна быть синхронизированна и выполнять одинаковое количество тактов.


 
Добежал   (2008-12-23 17:40) [56]


> Ну так и добавлять, через промежуточный буфер, который в
> результате будет блокироваться только на время добавления/извлечения
> данных из него.


хм... Ну да, логику понял.

Но мне все таки кажется она немного избыточной. А зачем по сути этот промежуточный буфер нужен? Ведь добавлять устройство можно непосредственно в список, по крайней мере у меня это происходит редко. Если добавления / отключения частые - то наверное выгоден промежуточный буфер.

А в какое время ты тогда предлагаешь, чтобы рабочий поток обращался к промежуточному буферу? Event тут не катит, поток и так постоянно работает, если есть хотя бы одно устройство. Каждый раз по прохождению всего списка лезть в промежуточный буфер?


 
vuk ©   (2008-12-23 18:11) [57]

Разделение нужно исключительно для минимизации времени блокировки ресурса. При этом потребитель работает со своим внутренним списком данных, который блокировать не нужно вообще. Event нужен, как признак наличия данных в общем промежуточном буфере, что позволит не дергать лишний раз блоировку буфера для провеерки количества элементов. А уж когда этот event щупать - дело десятое.


 
Leonid Troyanovsky ©   (2008-12-23 19:24) [58]


> Добежал   (23.12.08 15:02) [53]

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

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

--
Regards, LVT.


 
vuk ©   (2008-12-23 19:38) [59]

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


 
Leonid Troyanovsky ©   (2008-12-23 20:04) [60]


> vuk ©   (23.12.08 19:38) [59]

> Не нужно этого ничего. Промежуточный буфер и один event
> в нем проблему решают.

Если есть +один (в нем) event, то и буфер не нужен.
Если, конечно, комментарий ко мне :)

--
Regards, LVT.


 
vuk ©   (2008-12-23 21:00) [61]

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


 
Leonid Troyanovsky ©   (2008-12-23 21:09) [62]


> vuk ©   (23.12.08 21:00) [61]

> Что будет, если поток, обрабатывающий список, поставил блокировку
> и ушел в себя на некоторое время (кто его знает, что у него
> там внутри...)? Все будут его ждать?

А вот тут нужен mutex vs crit section with abandoned
или как его там.

--
Regards, LVT.



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

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

Наверх





Память: 0.69 MB
Время: 0.007 c
15-1230205417
тимохов
2008-12-25 14:43
2009.02.22
Почему в русской WinXP меню могут показываться кракозяблами?


15-1229958038
БарЛог
2008-12-22 18:00
2009.02.22
Форматы даты/времени


15-1230113488
Strannik_v76
2008-12-24 13:11
2009.02.22
Состав MS SQL Server 2005


11-1197356063
nikfel
2007-12-11 09:54
2009.02.22
Как отловить убирание с иконки мыши в трее.


2-1231824561
r900000
2009-01-13 08:29
2009.02.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
Английский Французский Немецкий Итальянский Португальский Русский Испанский