· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.WIN32 —› Критические секции

<< . 1 . 2 . 3 . 4 . 5 . >>

Посл.отвђт Сообщенiе


Дата: Янв 26, 2004 16:26:30

Grenader
У тебя просто талант популяризатора
Почему то я противоположного мнения. Но как говорит Serrgio -- это тоже нормально :)


Дата: Янв 26, 2004 16:49:11

Edmond
скромняга :)


Дата: Янв 26, 2004 22:24:14

Grenader> я лично использую cmpxchg.

Edmond> Да, команда есть такая, но она то поддерживается с какого пня?
Edmond> Не менее как с P6 (мне так кажется...)

Берем IA SDMv2:

IA-32 Architecture Compatibility
This instruction is not supported on Intel processors earlier than the Intel486 processors.


Дата: Янв 27, 2004 00:30:23

Edmond
Было интересно почитать, спасибо. Теперь буду пользоваться! Правда, есть один вопрос по этому методу:

;; Этот объект может быть ЛЮБЫМ объектом ядра для
;; синхронизации.


К понятию: "любой объект ядра для синхронизации", относятся события созданные API-функцией CreateEvent или Вы имели в виду что-то другое? Я новичок в этом, поэтому не пинайте меня сильно!


Дата: Апр 17, 2004 00:23:02 · Поправил: Oleg_SK

Привет всем!
Хорошо, так как возражений нет, значит я прав. Прошло некоторое время, и теперь я хочу продолжить этот топик. У меня появилось еще несколько вопросов:
1. Вопрос относится к критическим секциям, организованным стандартными средствами Win32 API. Допустим, два потока, имеющие разные приоритеты, ожидают своей очереди на вход в критическую секцию. Вопрос: будет ли ось принимать во внимание приоритеты ожидающих потоков, при открывании входа в освободившуюся критическую секцию одному из них?
2. Вопрос относится критическим секциям, организованным по методу, который описал Edmond. Я на досуге, набросал немного кода, чтобы протестировать этот метод.
Но, эта программка не захотела нормально работать. Вместо того, чтобы выводить сообщения от потоков по очереди (в любом порядке), она их выводит одновременно... В чем трабла? Покажите, пожалуйста, где я ошибся. Вот исходник программки (MASM32+Win32 API):
.686
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive

include windows.inc
include user32.inc
include kernel32.inc

includelib user32.lib
includelib kernel32.lib

ThreadProc_2 PROTO :DWORD
ThreadProc_3 PROTO :DWORD

.data
Header_1 db 'Thread 1',0
Text_1 db 'Hello!!!',0
Header_2 db 'Thread 2',0
Text_2 db 'Hello!!!',0
Header_3 db 'Thread 3',0
Text_3 db 'Hello!!!',0

.data?
hThread_2 DWORD ?
hThread_3 DWORD ?
HTHR_2 DWORD ?
HTHR_3 DWORD ?
hEvent DWORD ?
Flag DWORD ?; 0 - nonsignaled, 1 - signaled.

.code
Start:
push ebx
invoke CreateEvent, NULL, FALSE, TRUE, NULL
cmp eax, NULL
je Finish
mov [hEvent], eax
invoke CreateThread, NULL, NULL, addr [ThreadProc_2], NULL, NULL, addr [HTHR_2]
mov [hThread_2], eax
invoke CreateThread, NULL, NULL, addr [ThreadProc_3], NULL, NULL, addr [HTHR_3]
mov [hThread_3], eax
mov [Flag], 1

mov ebx, 5
Loop_1:
push ebx
xor eax, eax
xchg eax, [Flag]
test eax, eax
jnz Go
invoke WaitForSingleObject, [hEvent], INFINITE
cmp eax, WAIT_FAILED
je Finish
Go:
invoke MessageBox, 0, addr [Text_1], addr [Header_1], MB_OK

mov [Flag], 1
invoke SetEvent, [hEvent]
pop ebx
dec ebx
jnz Loop_1

Finish:
invoke Beep, 1500, 50
invoke WaitForMultipleObjects, 2, addr [hThread_2], TRUE, INFINITE
pop ebx
invoke ExitProcess, 0


ThreadProc_2 proc Parm_1:DWORD
push ebx
mov ebx, 5
Loop_2:
push ebx
xor eax, eax
xchg eax, [Flag]
test eax, eax
jnz Go2
invoke WaitForSingleObject, [hEvent], INFINITE
cmp eax, WAIT_FAILED
je Finish_2
Go2:
invoke MessageBox, 0, addr [Text_2], addr [Header_2], MB_OK

mov [Flag], 1
invoke SetEvent, [hEvent]
pop ebx
dec ebx
jnz Loop_2
Finish_2:
invoke Beep, 1500, 50
pop ebx
ret 4
ThreadProc_2 endp

ThreadProc_3 proc Parm_1:DWORD
push ebx
mov ebx, 5
Loop_3:
push ebx
xor eax, eax
xchg eax, [Flag]
test eax, eax
jnz Go3
invoke WaitForSingleObject, [hEvent], INFINITE
cmp eax, WAIT_FAILED
je Finish_3
Go3:
invoke MessageBox, 0, addr [Text_3], addr [Header_3], MB_OK

mov [Flag], 1
invoke SetEvent, [hEvent]
pop ebx
dec ebx
jnz Loop_3
Finish_3:
invoke Beep, 1500, 50
pop ebx
ret 4
ThreadProc_3 endp
end Start

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

Так он и так этим занимается.
(Хотя я нередко ввожу свою систему приоритетов. Но это уже темы для статей)

Идею я описал выше.


Я не совсем понял. Что значит: так он и так этим занимается? Дело в том, что я сейчас читаю книгу Джеффри Рихтера 'Создание эффективных Win32-приложений'. В главе 9, по поводу ожидающих функций (WaitForSingleObject \ WaitForMultipleObjects), автор пишет следующее:
Тут возникает интересный вопрос. Если несколько потоков ждет один объект ядра, какой из них пробудится при освобождении этого объекта? Официально Microsoft отвечает на этот вопрос так: "Алгоритм действует честно" Что это за алгоритм, Microsoft не говорит, потому что не хочет связывать себя обязательствами всегда придерживаться именно этого алгоритма. Она утверждает лишь одно - если объект ожидается несколькими потоками, то всякий раз, когда этот объект переходит в свободное состояние, каждый из них получает шанс на пробуждение.

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

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

Что Вы скажете на это?

Далее...

3. В свете вышесказанного, возникает главный вопрос: насколько эффективно применение этого способа по сравнению со стандартным подходом?

Как минимум в 1000 тактов лучше. Потому что если объект свободен, ты не вызываешь функции ядра Win32.


В выше упомянутой книге, автор пишет о том, что стандартные функции Win32 API, предназначенные для организации критических секций, не переводят систему в режим ядра. В свете этого, я не понимаю, о каких 1000 (как минимум) тактах процессора идет речь? В тоже время, ждущие функции (WaitForSingleObject \ WaitForMultipleObjects) переводят систему в режим ядра...

В общем, я понял так: способ организации критических секций, который вы показали, теоретически должен быть быстрее, только если критическая секция, на момент входа в нее, оказывается свободной. В противном случае, я предполагаю обратную картину...
Я решил сравнить описанный вами способ со стандартным способом. Для этого я поставил эксперимент. Написал программку. Процесс, порожденный ею, имел три потока и одну критическую секцию. Каждый из потоков имел по одному циклу, в телах которых организовывалась критическая секция с пустым телом. Все циклы делали несколько миллиардов итераций. Продолжительность работы программки в обоих случаях была примерно одинаковой, т.е. я невооруженным взглядом (я не пользовался ничем, кроме сикундомераJ)) разницы не заметил. В обоих случаях программка работала около 5 минут.

Заранее, спасибо за Ваши ответы.

P.S.:Ой , какое большое получилось сообщение 8-)


Дата: Апр 18, 2004 01:37:28

По поводу второго вопроса из предыдущего поста. У меня сложилось впечатление, что API-функция: WaitForSingleObject, делает почему-то не то, что я от нее ожидаю. Она, при установке объекта ядра: Event, в сигнальное состояние, делает планируемыми сразу все ожидающие потоки... IMHO, так быть не должно, ведь при создании объекта ядра: Event, он был сделан с авто-сбросом. Следовательно, по идее, при установке объекта ядра: Event, в сигнальное состояние, планируемым должен стать только один из ожидающих потоков. Кто ни будь может объяснить мне: что происходит?


Дата: Апр 18, 2004 03:35:43 · Поправил: Four-F

[ Oleg_SK: Но, эта программка не захотела нормально работать. Вместо того, чтобы выводить сообщения от потоков по очереди (в любом порядке), она их выводит одновременно... В чем трабла? ]

Ох... Давай посмотрим. Во-первых, программа работает как ожидается, т.е. что написано, то и получается.

Ты создаешь событие в свободном состоянии. Создаешь два потока, но они пока не выполняются, т.к. процессор занят твоим основным потоком. Основной поток проверяет флаг != 0, сбрасывая его в 0, идет на метку Go и тормозится на MessageBox'е. Процессор получает второй поток. Он пытается ждать на событии, но оно уже изначально свободно. Поэтому ожидание тут же удовлетворяется, событие при этом автоматически сбрасывается, и второй поток тормозится на MessageBox'е.

На этот момент ты имеешь два MessageBox'а: Thread 1 и Thread 2 и третий поток, который ждет. Дальше всё зависит на какой ты тыкешь. Допустим, мы тыкнули во второй. Устанавливаем флаг и событие и идем на Loop_2. Проверяем флаг и т.к. он != 0 обходим WaitForSingleObject и тормозимся на MessageBox'е Thread 2. Третий поток уже давно ждет событие, он и получает управление, показывая MessageBox Thread 3.

Дальше опять всё зависит от того, какой поток отпустить.


Дата: Апр 18, 2004 13:25:20

Привет всем!
Four-F
Спасибо Вам за ответ. Действительно, после создания объекта ядра: Event, он принимает свободное состояние. Это ошибка. Судя по всему, он должен после создания переходить в занятое состояние. Недоглядел я.

Помимо той ошибки, на которую указал Four-F, я обнаружил еще одну. Дело в том, что в то время когда какой либо из потоков просыпается после API-функции: WaitForSingleObject, тем самым, становясь владельцем КС, я никак не отмечаю этот факт в переменной Flag, т.е. КС остается открытой...
После того, как я исправил указанные ошибки, программа стала работать правильно:)

Edmond
В своем позапрошлом посте, я указал неверные результаты полученные после тестирования. На самом деле результаты были получены другие и они просто ошеломляющи:
1) Стандартный подход к созданию КС: ~26 сек.;
2) Способ, который Вы описали: ~3 мин. 45 сек,
т.е., на лицо, более чем восьмикратное превосходство стандартного подхода. Microsoft for ever!!! Честно говоря, я не ожидал ТАКИХ результатов! Если все это верно, то я не вижу смысла применять указанный Вами метод. Исключением здесь является тот случай, когда критические секции нужны для обеспечения меж процессной синхронизации. Далее я выкладываю два листинга программ, которые использовались при тестировании:

Листинг (стандартный подход).
.686
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive

include windows.inc
include kernel32.inc

includelib kernel32.lib

ThreadProc_2 PROTO :DWORD
ThreadProc_3 PROTO :DWORD


.data

.data?
hThread_2 DWORD ?
hThread_3 DWORD ?
HTHR_2 DWORD ?
HTHR_3 DWORD ?
sCS CRITICAL_SECTION <?>

.code
Start:
push ebx
invoke InitializeCriticalSection, addr sCS
invoke CreateThread, NULL, NULL, addr ThreadProc_2, NULL, NULL, addr HTHR_2
mov hThread_2, eax
invoke CreateThread, NULL, NULL, addr ThreadProc_3, NULL, NULL, addr HTHR_3
mov hThread_3, eax

mov ebx, 05FFFFFFh
Loop_1:
push ebx

xor eax, eax
xor eax, eax
xor eax, eax

invoke EnterCriticalSection, addr sCS

xor eax, eax
xor eax, eax
xor eax, eax

invoke LeaveCriticalSection, addr sCS

xor eax, eax
xor eax, eax
xor eax, eax

pop ebx
dec ebx
jnz Loop_1

invoke WaitForMultipleObjects, 2, addr [hThread_2], TRUE, INFINITE
invoke Beep, 2500, 50
pop ebx
invoke ExitProcess, 0

ThreadProc_2 proc Parm_1:DWORD
push ebx
mov ebx, 05FFFFFFh
Loop_2:
push ebx

xor eax, eax
xor eax, eax
xor eax, eax

invoke EnterCriticalSection, addr sCS

xor eax, eax
xor eax, eax
xor eax, eax

invoke LeaveCriticalSection, addr sCS

xor eax, eax
xor eax, eax
xor eax, eax

pop ebx
dec ebx
jnz Loop_2
pop ebx
ret 4
ThreadProc_2 endp

ThreadProc_3 proc Parm_1:DWORD
push ebx
mov ebx, 05FFFFFFh
Loop_3:
push ebx

xor eax, eax
xor eax, eax
xor eax, eax

invoke EnterCriticalSection, addr sCS

xor eax, eax
xor eax, eax
xor eax, eax

invoke LeaveCriticalSection, addr sCS

xor eax, eax
xor eax, eax
xor eax, eax

pop ebx
dec ebx
jnz Loop_3
pop ebx
ret 4
ThreadProc_3 endp
end Start


Листинг (метод, который описал Edmond).
.686
.model flat, stdcall ;32 bit memory model
option casemap :none ;case sensitive

include windows.inc
include kernel32.inc

includelib kernel32.lib

ThreadProc_2 PROTO :DWORD
ThreadProc_3 PROTO :DWORD

.data

.data?
hThread_2 DWORD ?
hThread_3 DWORD ?
HTHR_2 DWORD ?
HTHR_3 DWORD ?
hEvent DWORD ?
Flag DWORD ?; 0 - nonsignaled, 1 - signaled.

.code
Start:
push ebx
invoke CreateEvent, NULL, FALSE, FALSE, NULL
cmp eax, NULL
je Finish
mov [hEvent], eax
invoke CreateThread, NULL, NULL, addr [ThreadProc_2], NULL, NULL, addr [HTHR_2]
mov [hThread_2], eax
invoke CreateThread, NULL, NULL, addr [ThreadProc_3], NULL, NULL, addr [HTHR_3]
mov [hThread_3], eax
mov [Flag], 1

mov ebx, 05FFFFFFh
Loop_1:
push ebx

xor eax, eax
xor eax, eax
xor eax, eax

xor eax, eax
xchg eax, [Flag]
test eax, eax
jnz Go
invoke WaitForSingleObject, [hEvent], INFINITE
mov [Flag], 0
Go:
xor eax, eax
xor eax, eax
xor eax, eax

mov [Flag], 1
invoke SetEvent, [hEvent]

xor eax, eax
xor eax, eax
xor eax, eax

pop ebx
dec ebx
jnz Loop_1

Finish:
invoke WaitForMultipleObjects, 2, addr [hThread_2], TRUE, INFINITE
invoke Beep, 2500, 50
pop ebx
invoke ExitProcess, 0


ThreadProc_2 proc Parm_1:DWORD
push ebx
mov ebx, 05FFFFFFh
Loop_2:
push ebx

xor eax, eax
xor eax, eax
xor eax, eax

xor eax, eax
xchg eax, [Flag]
test eax, eax
jnz Go2
invoke WaitForSingleObject, [hEvent], INFINITE
mov [Flag], 0
Go2:
xor eax, eax
xor eax, eax
xor eax, eax

mov [Flag], 1
invoke SetEvent, [hEvent]

xor eax, eax
xor eax, eax
xor eax, eax

pop ebx
dec ebx
jnz Loop_2
Finish_2:
pop ebx
ret 4
ThreadProc_2 endp

ThreadProc_3 proc Parm_1:DWORD
push ebx
mov ebx, 05FFFFFFh
Loop_3:
push ebx

xor eax, eax
xor eax, eax
xor eax, eax

xor eax, eax
xchg eax, [Flag]
test eax, eax
jnz Go3
invoke WaitForSingleObject, [hEvent], INFINITE
mov [Flag], 0
Go3:
xor eax, eax
xor eax, eax
xor eax, eax

mov [Flag], 1
invoke SetEvent, [hEvent]

xor eax, eax
xor eax, eax
xor eax, eax

pop ebx
dec ebx
jnz Loop_3
Finish_3:
pop ebx
ret 4
ThreadProc_3 endp
end Start

Конфигурация компьютера, на котором производилось тестирование:
Motherboard: ASUS CUSL2-C
CPU: PentiumIII (Coppermine) 1Ghz (up to 1.05Ghz)
RAM: 256Mb (PC133)
HDD: 30Gb Maxtor Diamond Max Plus 60
Video: Inno3D GeForce2Pro 64Mb DDR
Sound: Creative SB Live! Value
CD-ROM: ASUS 50x-speed
FDD: Samsung
OS: Windows 2000 Pro Russian (SP3)

Заранее благодарен за Ваши ответы!

P.S. Если я ошибаюсь, то поправьте меня.


Дата: Апр 18, 2004 21:41:40 · Поправил: Four-F

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

Критическая секция - это объект внутрипроцессной синхронизации.


Дата: Апр 19, 2004 13:46:38

Four-F
Критическая секция - это объект внутри процессной синхронизации.
Стандартная - да, а сделанная по описанию Edmond по идее можно применять и для меж процессной синхронизации (если у процессов есть общая память). Ведь IMHO внутри процессность это скорее недостаток, чем достоинство...


Дата: Апр 19, 2004 13:55:36

Four-F
Критическая секция - это объект внутри процессной синхронизации.
Или Вы имели в виду, что критические секции по своей концепции являются внутри-прцессными объектами синхронизации и применять их для межпроцессной синхронизации (даже если это возможно) смысла нет?


Дата: Апр 19, 2004 15:16:51

Я имел ввиду стандартную, которую система предлагает. У того же Рихтера есть его собственная реализация межпроцессной критической секции - Optex - в книге подробно описана. Сам я ей не баловался и сказать ничего не могу - попробуй. Вообще в системе такое кол-во всяческих объектов синхронизации, что изобретать свои, IMHO, себе дороже.


Дата: Апр 19, 2004 19:40:09

Four-F
Вообще в системе такое кол-во всяческих объектов синхронизации, что изобретать свои, IMHO, себе дороже.
:)))
В общем: согласен. Я решил поковыряться с КС, сделанными как описал Edmond только по той причине, что считал, что применение ассемблера в данном случае поможет значительно повысить производительность. К сожалению, это не подтвердилось (во всяком случае: при внутри процессной синхронизации). Об Optex’ах пока ничего не знаю, т.к. не дошел пока до их описания. Надо будет, потом сравнить эти способы…


Дата: Май 4, 2004 02:38:17 · Поправил: Oleg_SK

Привет всем!
Кстати, Рихтер, в своей книге 'Windows для профессионалов', в 9 или 10 главе признается в том, что ранее он вводил нас в заблуждение, когда говорил о том, что при обработке критических секций ОСь не переходит в режим ядра... Блин, если для ожидания функция входа в КС все равно переходит в режим ядра, то мне обсалютно непонятно: откуда взялась такая разница в производительности приведенных тестовых прог?
Заранее благодарен вам за ответы!


Дата: Май 4, 2004 03:16:27 · Поправил: rst

oleg_sk
typedef struct _RTL_CRITICAL_SECTION {
    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

    //
    //  The following three fields control entering and exiting the critical
    //  section for the resource
    //

    LONG LockCount;
    LONG RecursionCount;
    HANDLE OwningThread;        // from the thread's ClientId->UniqueThread
    HANDLE LockSemaphore;
    DWORD SpinCount;
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;


Не надо тут этого

Переход в режим ядра происходит в двух случаях :
1) Захват секции
2) Ожидание.
причем если можно сказать. что при захвате секции мы теряем время впустую (переход в ядро)
то при ожидании - ничего не теряется. Т.к. поток будет все равно ждать.
В случае если происходит попытка захвата критической секции потоком, который итак ею владеет (такое часто бывает), то перехода в режим ядра не произойдет.

<< . 1 . 2 . 3 . 4 . 5 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.101