Пятница, 25.07.2025, 19:05 Приветствую Вас Гость

On-line: Книги, учебники, статьи

Главная | Регистрация | Вход | RSS

Глава 2. Многозадачное программирование(3)

III

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

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

Часто случается, что данные невозможно получать периодически. Они могут, например, зависеть от деятельности третьего процесса. Как же второй процесс узнает, что данные уже готовы для передачи? На первый взгляд проблема решается введением дополнительной переменной, назовем ее FLAG. Примем, что при FLAG=0 данные не готовы, а при FLAG=1 данные готовы. Далее действует весьма простая схема.

NO_DAT:
 CMP FLAG,1
 JNE NO_DAT
 ... ;передача данных
 ...
 MOV FLAG,0
 ...

Это фрагмент, как Вы понимаете, для второго потока. Первый же поток также должен проверять переменную FLAG и, если FLAG=0, поместить новые данные и установить значение переменной FLAG, равное единице. Данная схема совсем не плоха, например, когда один процесс ждет окончания работы другого процесса. Другими словами, данные являются результатом всей работы этого процесса. Например, запущен компилятор, а другой поток ждет окончания его работы, дабы вывести результаты этой работы на некое устройство. Эта ситуация весьма распространена, но все же случай этот частный.

А что будет, если процесс передачи данных должен производится многократно? Легко видеть, что данная схема будет работать и в более сложном случае. Важно, однако, чтобы второй процесс менял содержимое FLAG только после того, как он возьмет данные, а первый процесс - после того, как положит данные. Если нарушить это правило, то может возникнуть коллизия, когда, например, второй процесс еще не взял данные, а они уже изменились.

Такой подход можно осуществить и в более общем случае, когда два потока (или - два процесса) должны поочередно получать доступ к одному ресурсу. Как легко видеть, данный подход предполагает, что ресурс открыт либо для одного, либо для другого процесса. Если бы поставить задачу несколько иным образом - процесс либо открыт, либо закрыт для доступа, то возникло бы некоторое затруднение. Действительно, вероятна такая ситуация, когда оба потока ожидают открытия ресурса. Другими словами, они непрерывно осуществляют проверку переменной FLAG (CMP FLAG,1). Может статься, что они оба почти одновременно обратятся к ресурсу. Совершенно ясно, что здесь возникает необходимость в "третьей силе", которая бы занималась распределением доступа к ресурсу. Например, посылала бы сообщение вначале одному потоку и, если он ожидает доступа, давала доступ именно ему, а затем подобный процесс повторяется со вторым потоком.

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

Оставшаяся часть данного раздела будет всецело посвящена средствам синхронизации операционной системы Windows.

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

CreateSemaphore - создает глобальный объект-семафор. Возвращает дескриптор семафора. Параметры функции:

  • 1-й параметр. Указатель на структуру, определяющую атрибуты доступа. Может иметь значение для Windows NT. Обычно данный параметр полагается равным NULL.
  • 2-й параметр. Начальное значение счетчика семафора. Определяет, сколько задач имеют доступ к ресурсу вначале.
  • 3-й параметр. Количество задач, которые имеют одновременный доступ к ресурсу.
  • 4-й параметр. Указатель на строку, содержащую имя семафора.

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

  • 1-й параметр. Определяет желаемый уровень доступа к семафору. Возможные значения:
    SEMAPHORE_MODIFY_STATE = 2Н, разрешить использование функции ReleaseSemaphore,
    SYNCHRONIZE = 100000Н, разрешить использование любой функции ожидания, только для Windows NT,
    SEMAPHORE_ALL_ACCESS = 0F0000h + SYNCHRONIZE + 3H, специфицирует все возможные флаги доступа к семафору.
  • (в книге пропущен) 2-й параметр. Указывает, может ли наследоваться дескриптор семафора, возвращаемый данной функцией, процессом, созаваемым функцией CreateProcess; 0 - не может.
  • (также в книге пропущен) 3-й параметр. Указатель на ASCIIZ-строку, содержащую имя семафора.

WaitForSingleObject - ожидать открытие семафора. При успешном завершении, т.е. открытии доступа к объекту, функция возвращает 0. Значение 102h будет означать, что закончился заданный период ожидания. Параметры функции:

  • 1-й параметр. Дескриптор семафора.
  • 2-й параметр. Время ожидания в миллисекундах. Если параметр равен INFINITE = 0FFFFFFFFh, то время ожидания не ограничено.

ReleaseSemaphore - освободить семафор и позволить получить доступ к ресурсу другим процессам. Параметры функции:

  • 1-й параметр. Дескриптор семафора.
  • 2-й параметр. Определяет, какое значение должно быть добавлено к счетчику семафора. Чаще всего этот параметр равен единице.
  • 3-й параметр. Указатель на переменную, куда должно быть помещено предыдущее значение счетчика.

Рассмотрим алгоритм работы с семафором. Сначала при помощи функции CreateSemaphore создается семафор и его дескриптор присваивается глобальной переменной. Перед попыткой обращения к ресурсам, доступ к которым необходимо ограничить, поток должен вызвать функцию WaitForSingleObject. При открытии доступа функция возвращает 0. По окончании работы с ресурсом следует вызвать функцию ReleaseSemaphore. Тем самым увеличивается счетчик доступа на 1. С помощью семафора можно регулировать количество потоков, которые одновременно могут иметь доступ к ресурсу. Максимальное значение счетчика как раз и определяет, сколько потоков могут получить доступ к ресурсу одновременно. Но обычно, как я уже говорил, максимальное значение полагают равным 1.

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

CreateEvent - создает объект-событие. Параметры функции.
1-й параметр. Имеет тот же смысл, что и первый параметр функции CreateSemaphore. Обычно полагается равным NULL.
2-параметр. Если параметр не равен нулю, то событие может быть сброшено при помощи функции ResetEvent. Иначе событие сбрасывается при доступе к нему какого либо процесса.
3-й параметр. Если параметр равен 0, то событие инициализируется как сброшенное, в противном случае сразу же подается сигнал о наступлении соответствующей ситуации.
4-й параметр. Указатель на строку, которая содержит имя события.

Ожидание события осуществляется, как и в случае с семафором, функцией WaitForSingleObject.

Функция OpenEvent аналогична функции OpenSemaphore, и на ней мы останавливаться не будем.

SetEvent - подать сигнал о наступлении события. Параметры функции.
1-й параметр. Дескриптор события.

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

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

Рис. 3.2.3. Пример синхронизации двух потоков посредством критической секции.



Вход на сайт
Поиск
Календарь
«  Июль 2025  »
ПнВтСрЧтПтСбВс
 123456
78910111213
14151617181920
21222324252627
28293031
Архив записей
Наш опрос
Как Вам удобнее??
Всего ответов: 341
Мини-чат
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Статистика

    Онлайн всего: 2
    Гостей: 2
    Пользователей: 0