Ассемблер для Windows

Критические секции



Критические секции.

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

InitializeCriticalSection - данная функция создает объект под названием критическая секция. Параметры функции.

  • 1-й параметр. Указатель на структуру, указанную ниже. Поля данной структуры используются только внутренними процедурами, и смысл их безразличен.
  • CRITICAL_SECTION STRUCT DebugInfo DWORD ? LockCount LONG ? RecursionCount LONG ? OwningThread HANDLE ? LockSemaphore HANDLE ? SpinCount DWORD ? CRITICAL_SECTION ENDS

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

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

    DeleteCriticalSection - удалить объект "критическая секция". Параметр аналогичен предыдущим.

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

    Теперь рассмотрим пример использования критической секции. Примеры использования семафоров и событий Вы сможете найти в книге [4]. Изложим вкратце идею, положенную в основу примера на Рисунок 3.2.3.

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

    Часть процедуры, выводящей очередной символ, сделана критической, поэтому доступ к выводу в окно в данный момент времени имеет только один поток.






    ; файл thread2.inc ; константы ; сообщение приходит при закрытии окна WM_DESTROY equ 2 ; сообщение приходит при создании окна WM_CREATE equ 1 ; сообщение при щелчке левой кнопкой мыши в области окна WM_LBUTTONDOWN equ 201h ; сообщение при щелчке правой кнопкой мыши в области окна WM_RBUTTONDOWN equ 204h ; свойства окна CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_OVERLAPPEDWINDOW equ 000CF0000H stylcl equ CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS DX0 equ 300 DY0 equ 200 ; компоненты цветов RED equ 50 GREEN equ 50 BLUE equ 255 RGBW equ (RED or (GREEN shl 8)) or (BLUE shl 16) RGBT equ 255 ; красный ; идентификатор стандартной иконки IDI_APPLICATION equ 32512 ; идентификатор курсора IDC_CROSS equ 32515 ; режим показа окна - нормальный SW_SHOWNORMAL equ 1

    ; прототипы внешних процедур IFDEF MASM EXTERN Sleep@4:NEAR EXTERN CreateThread@24:NEAR EXTERN InitializeCriticalSection@4:NEAR EXTERN EnterCriticalSection@4:NEAR EXTERN LeaveCriticalSection@4:NEAR EXTERN DeleteCriticalSection@4:NEAR EXTERN GetTextExtentPoint32A@16:NEAR EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4:NEAR EXTERN UpdateWindow@4:NEAR EXTERN TextOutA@20:NEAR EXTERN CreateSolidBrush@4:NEAR EXTERN SetBkColor@8:NEAR EXTERN SetTextColor@8:NEAR EXTERN GetDC@4:NEAR EXTERN DeleteDC@4:NEAR ELSE EXTERN Sleep:NEAR EXTERN CreateThread:NEAR EXTERN InitializeCriticalSection:NEAR EXTERN EnterCriticalSection:NEAR EXTERN LeaveCriticalSection:NEAR EXTERN DeleteCriticaiSection:NEAR EXTERN GetTextExtentPoint32A:NEAR EXTERN CreateWindowExA:NEAR EXTERN DefWindowProcA:NEAR EXTERN DispatchMessageA:NEAR EXTERN ExitProcess:NEAR EXTERN GetMessageA:NEAR EXTERN GetModuleHandleA:NEAR EXTERN LoadCursorA:NEAR EXTERN LoadIconA:NEAR EXTERN PostQuitMessage:NEAR EXTERN RegisterClassA:NEAR EXTERN ShowWindow:NEAR EXTERN TranslateMessage:NEAR EXTERN UpdateWindow:NEAR EXTERN TextOutA:NEAR EXTERN CreateSolidBrush:NEAR EXTERN SetBkColor:NEAR EXTERN SetTextColor:NEAR EXTERN GetDC:NEAR EXTERN DeleteDC:NEAR



    Sleep@4 = Sleep CreateThread@24 = CreateThread InitializeCriticalSection@4 = InitializeCriticalSection EnterCriticalSection@4 = EnterCriticalSection LeaveCriticalSection@4 = LeaveCriticalSection DeleteCriticalSection@4 = DeleteCriticaiSection GetTextExtentPoint32A@16 = GetTextExtentPoint32A CreateWindowExA@48 = CreateWindowExA DefWindowProcA@16 = DefWindowProcA DispatchMessageA@4 = DispatchMessageA ExitProcess@4 = ExitProcess GetMessageA@16 = GetMessageA GetModuleHandleA@4 = GetModuleHandleA LoadCursorA@8 = LoadCursorA LoadIconA@8 = LoadIconA PostQuitMessage@4 = PostQuitMessage RegisterClassA@4 = RegisterClassA ShowWindow@8 = ShowWindow TranslateMessage@4 = TranslateMessage UpdateWindow@4 = UpdateWindow TextOutA@20 = TextOutA CreateSolidBrush@4 = CreateSolidBrush SetBkColor@8 = SetBkColor SetTextColor@8 = SetTextColor GetDC@4 = GetDC DeleteDC@4 = DeleteDC ENDIF

    ; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ;-------------

    WNDCLASS STRUC CLSSTYLE DD ? CLSLPFNWNDPROC DD ? CLSCBCLSEXTRA DD ? CLSCBWNDEXTRA DD ? CLSHINSTANCE DD ? CLSHICON DD ? CLSHCURSOR DD ? CLSHBRBACKGROUND DD ? MENNAME DD ? CLSNAME DD ? WNDCLASS ENDS

    ; структура для работы с критической секцией CRIT STRUC DD ? DD ? DD ? DD ? DD ? DD ? CRIT ENDS

    ; структура для определения длины текста SIZET STRUC X1 DWORD ? Y1 DWORD ? SIZET ENDS

    ; файл thread2.asm .386P ; плоская модель .MODEL FLAT, stdcall ;------------------------------------------------ include thread2.inc ; подключения библиотек IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\gdi32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------

    ; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> SZT SIZET <?> HINST DD 0 TITLENAME DB 'Вывод в окно двумя потоками',0 NAM DB 'CLASS32',0 XT DWORD 30 YT DWORD 30 HW DD ? DC DD ? TEXT DB 'Текст в окне красный',0 SPA DB ' ' DB ' ',0 IND DD 0 SK CRIT <?> THR1 DD ? THR2 DD ? FLAG1 DD 0 FLAG2 DD 0 _DATA ENDS



    ; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ; заполнить структуру окна ; стиль MOV [WC.CLSSTYLE],stylcl ; процедура обработки сообщений MOV [WC.CLSLPFNWNDPROC],OFFSET WNDPROC MOV [WC.CLSCBCLSEXTRA],0 MOV [WC.CLSCBWNDEXTRA],0 MOV EAX,[HINST] MOV [WC.CLSHINSTANCE],EAX ;----------иконка окна PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ;----------курсор окна PUSH IDC_CROSS PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ;---------- PUSH RGBW ; цвет кисти CALL CreateSolidBrush@4 ; создать кисть MOV [WC.CLSHBRBACKGROUND],EAX MOV DWORD PTR [WC.MENNAME],0 MOV DWORD PTR [WC.CLSNAME],OFFSET NAM PUSH OFFSET WC CALL RegisterClassA@4 ; создать окно зарегистрированного класса PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH DY0 ; DY0 - высота окна PUSH DX0 ; DX0 - ширина окна PUSH 100 ; координата Y PUSH 100 ; координата X PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET TITLENAME ; имя окна PUSH OFFSET NAM ; имя класса PUSH 0 CALL CreateWindowExA@48 ; проверка на ошибку CMP EAX,0 JZ _ERR MOV [NEWHWND], EAX ; дескриптор окна ;------------------------------------ PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ; показать созданное окно ;------------------------------------ PUSH [NEWHWND] CALL UpdateWindow@4 ;перерисовать видимую часть окна

    ; петля обработки сообщений MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP AX, 0 JE END_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: ; выход из программы (закрыть процесс) PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP ;-------------------------------------

    ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10Н] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI CMP DWORD PTR [EBP+0CH],WM_DESTROY JE WMDESTROY CMP DWORD PTR [EBP+0CH],WM_CREATE JE WMCREATE CMP DWORD PTR [EBP+0CH],WM_LBUTTONDOWN JNE CONTIN ; проверить флаг запуска CMP FLAG1,0 JNE DEFWNDPROC MOV FLAG1,1 ; инициализировать указатели LEA EAX,TEXT MOV IND,EAX MOV XT,30 ; запуск первого потока PUSH OFFSET THR1 PUSH 0 PUSH EAX PUSH OFFSET THREAD1 PUSH 0 PUSH 0 CALL CreateThread@24 ; запуск второго потока PUSH OFFSET THR2 PUSH 0 PUSH EAX PUSH OFFSET THREAD2 PUSH 0 PUSH 0 CALL CreateThread@24 JMP DEFWNDPROC CONTIN: CMP DWORD PTR [EBP+0CH],WM_RBUTTONDOWN JNE DEFWNDPROC ; проверить флаг запуска CMP FLAG2,0 JNE DEFWNDPROC MOV FLAG2,1 ; инициализировать указатели LEA EAX,SPA MOV IND,EAX MOV XT,30 ; запуск первого потока PUSH OFFSET THR1 PUSH 0 PUSH EAX PUSH OFFSET THREAD1 PUSH 0 PUSH 0 CALL CreateThread@24 ; запуск второго потока PUSH OFFSET THR2 PUSH 0 PUSH EAX PUSH OFFSET THREAD2 PUSH 0 PUSH 0 CALL CreateThread@24 JMP DEFWNDPROC WMCREATE: MOV EAX,DWORD PTR [EBP+08H] ; запомнить дескриптор окна в глобальной переменной MOV HW,EAX ; инициализировать критическую секцию PUSH OFFSET SK CALL InitializeCriticalSection@4 MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: ; удалить критическую секцию PUSH OFFSET SK CALL DeleteCriticalSection@4 PUSH 0 CALL PostQuitMessage@4 ; WM_QUIT MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP



    ; вывод OUTSTR PROC ; проверяем, не закончился ли текст MOV EBX,IND CMP BYTE PTR [EBX],0 JNE NO_0 RET NO_0: ; вход в критическую секцию PUSH OFFSET SK CALL EnterCriticalSection@4 ;----------------- PUSH HW CALL GetDC@4 MOV DC,EAX ;---------------- цвет фона = цвет окна PUSH RGBW PUSH EAX CALL SetBkColor@8 ;---------------- цвет текста (красный) PUSH RGBT PUSH DC CALL SetTextColor@8 ;---------------- вывести текст PUSH 1 PUSH IND PUSH YT PUSH XT PUSH DC CALL TextOutA@20 ;- вычислить длину текста в пикселях текста PUSH OFFSET SZT PUSH 1 PUSH IND PUSH DC CALL GetTextExtentPoint32A@16 ; увеличить указатели MOV EAX,SZT.X1 ADD XT,EAX INC IND ;---------------- закрыть контекст PUSH DC CALL DeleteDC@4 ; выход из критической секции PUSH OFFSET SK CALL LeaveCriticalSection@4 RET OUTSTR ENDP

    ; первый поток THREAD1 PROC L01: ; проверить, не конец ли текста MOV EBX,IND CMP BYTE PTR [EBX],0 JE _END1 ; вывод очередного символа CALL OUTSTR ; задержка PUSH 1000 CALL Sleep@4 JMP L01 _END1: RET 4 THREAD1 ENDP

    ; второй поток THREAD2 PROC L02: ; проверить, не конец ли текста MOV EBX,IND CMP BYTE PTR [EBX],0 JE _END2 ; вывод очередного символа CALL OUTSTR ; задержка PUSH 1000 CALL Sleep@4 JMP L02 _END2: RET 4 THREAD2 ENDP _TEXT ENDS END START


    Содержание раздела