платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 16 - Объект события
Страница 1 из 1
платформа Win32 - Туториалы Iczelion'a на русском, адаптированные для FreeBasic. Урок 16 - Объект события
Win32 API. Урок 16. Объект события
Мы изучим, что такое объект события и как использовать его в мультитредной программе.
Теория:
В предыдущем туториале я продемонстрировал, как треды взаимодействуют друг с другом через собственные windows-сообщения. Я пропустил два других метода:
глобальная переменная и объект события. В этом туториале мы используем оба.
Объект события - это что-то вроде переключателя: у него есть только два состояния: вкл и выкл. Вы создаете объект события и помещаете его в коде соответствующего треда, где наблюдаете за состоянием объекта. Если объект события выключен, ждущие его треды "спят". В подобном состоянии треды мало загружают CPU.
Вы можете создать объект события, вызвав функцию CreateEvent, которая имеет следующий синтаксис:
function CreateEvent _
(byval lpEventAttributes as LPSECURITY_ATTRIBUTES, _
byval bManualReset as BOOL, _
byval bInitialState as BOOL, _
byval lpName as LPCSTR) as HANDLE
• lpEventAttributes --> Если вы укажете значение NULL, у создаваемого объекта будут установки безопасности по умолчанию.
• bManualReset --> Если вы хотите, чтобы Windows автоматически переключал объект события в "выключено", вы должны присвоить этому параметру значение FALSE. Иначе вам надо будет выключить объект вручную с помощью вызова ResetEvent.
• bInitialState --> Если вы хотите, чтобы объект события при создании был установлен в положение "включено", укажите TRUE в качестве данного параметра, в противном случае объект события будет установлен в положение "выключен".
lpName --> Указатель на ASCIIZ-строку, которая будет именем объекта события. Это имя будет использоваться, когда вы захотите вызвать OpenEvent.
Если вызов прошел успешно, CreateEvent возвратит хэндл на созданный объект события. В противном случае она возвратит NULL.
Вы можете изменять состояние объекта события с помощью двух API-функций: SetEvent и ResetEvent. Функция SetEvent устанавливает объект события в положение "включено". ResetEvent делает обратное.
Когда объект события создан, вы должны поместить вызов функции WaitForSingleObject в тред, который должен следить за состоянием объекта события. Эта функция
имеет следующий синтаксис:
function WaitForSingleObject _
(byval hObject as HANDLE, _
byval dwTimeout as DWORD) as DWORD
• hObject --> Хэндл одного из синхронизационных объектов. Объект события - это вид синхронизационного события.
• dwTimeout --> Указывает в миллисекундах время, которое эта функция будет ждать, пока объект события не перейдет во включенное состояние. Если указанное время пройдет, а объект события все еще выключен, WaitForSingleObject вернет управление. Если вы хотите, чтобы функция наблюдала за объектом бесконечно, вы должны указать значение INFINITE в качестве этого параметра.
Пример:
Нижеприведенный пример отображает окно, ожидающее пока пользователь не выберет какую-нибудь команду из меню. Если пользователь выберет "run thread", тред
начнет подсчет. Когда он закончит, появится сообщение, информирующее пользователя о том, что работа выполнена. Во время того, как проводится подсчет, пользователь
может выбрать команду "stop thread", чтобы остановить тред.
файл objevent.bas
файл objevent.bi
файл objevent.rc
Анализ:
В этом примере я демонстрирую другую технику работы с тредами.
case WM_CREATE
hEventStart = CreateEvent _
(NULL, _
FALSE, _
FALSE, _
NULL)
hThread = CreateThread _
(NULL, _
NULL, _
cPtr(LPTHREAD_START_ROUTINE,@ThreadProc), _
NULL, _
0, _
@ThreadID)
CloseHandle _
(hThread)
Вы можете видеть, что я создал объект события и тред во время обработки сообщения WM_CREATE. Я создаю объект события, установленного в состояние "выключено" и обладающего свойством автоматического выключения. После того, как объект события создан, я создаю тред. Тем не менее, тред не начинает выполняться немедленно, так как он ждет, пока не включится объект события:
sub ThreadProc(byval param as uInteger)
dim in as integer
dim de as integer
do
de=600000000
WaitForSingleObject _ '
(hEventStart, _
INFINITE)
WaitForSingleObject. ждет, пока не включится объект события, а затем возвращается. Это означает, что даже если тред создан, мы помещаем его в спящее состояние. Когда пользователь выбирает в меню команду "run thread", мы включаем объект события:
case IDM_START_THREAD
SetEvent _
(hEventStart)
Вызов SetEvent включает объект события, после чего WaitForSingleObject возвращается и тред начинает выполняться. Когда пользователь выбирает команду "stop thread", мы устанавливаем значение глобальной переменной в TRUE.
if EventStop <> TRUE then
in +=1
de -=1
in -=3
else
MessageBox(hwnd,@StopString,@AppName,MB_OK)
EventStop = FALSE
continue do
end if
Это останавливает тред и снова передает управление функции WaitForSingleObject. Заметьте, что мы не должны вручную выключать объект, так как мы указали при вызове функции CreateEvent, что значение bManualReset равно FALSE.
[C] Iczelion, пер. Aquila.
Мы изучим, что такое объект события и как использовать его в мультитредной программе.
Теория:
В предыдущем туториале я продемонстрировал, как треды взаимодействуют друг с другом через собственные windows-сообщения. Я пропустил два других метода:
глобальная переменная и объект события. В этом туториале мы используем оба.
Объект события - это что-то вроде переключателя: у него есть только два состояния: вкл и выкл. Вы создаете объект события и помещаете его в коде соответствующего треда, где наблюдаете за состоянием объекта. Если объект события выключен, ждущие его треды "спят". В подобном состоянии треды мало загружают CPU.
Вы можете создать объект события, вызвав функцию CreateEvent, которая имеет следующий синтаксис:
function CreateEvent _
(byval lpEventAttributes as LPSECURITY_ATTRIBUTES, _
byval bManualReset as BOOL, _
byval bInitialState as BOOL, _
byval lpName as LPCSTR) as HANDLE
• lpEventAttributes --> Если вы укажете значение NULL, у создаваемого объекта будут установки безопасности по умолчанию.
• bManualReset --> Если вы хотите, чтобы Windows автоматически переключал объект события в "выключено", вы должны присвоить этому параметру значение FALSE. Иначе вам надо будет выключить объект вручную с помощью вызова ResetEvent.
• bInitialState --> Если вы хотите, чтобы объект события при создании был установлен в положение "включено", укажите TRUE в качестве данного параметра, в противном случае объект события будет установлен в положение "выключен".
lpName --> Указатель на ASCIIZ-строку, которая будет именем объекта события. Это имя будет использоваться, когда вы захотите вызвать OpenEvent.
Если вызов прошел успешно, CreateEvent возвратит хэндл на созданный объект события. В противном случае она возвратит NULL.
Вы можете изменять состояние объекта события с помощью двух API-функций: SetEvent и ResetEvent. Функция SetEvent устанавливает объект события в положение "включено". ResetEvent делает обратное.
Когда объект события создан, вы должны поместить вызов функции WaitForSingleObject в тред, который должен следить за состоянием объекта события. Эта функция
имеет следующий синтаксис:
function WaitForSingleObject _
(byval hObject as HANDLE, _
byval dwTimeout as DWORD) as DWORD
• hObject --> Хэндл одного из синхронизационных объектов. Объект события - это вид синхронизационного события.
• dwTimeout --> Указывает в миллисекундах время, которое эта функция будет ждать, пока объект события не перейдет во включенное состояние. Если указанное время пройдет, а объект события все еще выключен, WaitForSingleObject вернет управление. Если вы хотите, чтобы функция наблюдала за объектом бесконечно, вы должны указать значение INFINITE в качестве этого параметра.
Пример:
Нижеприведенный пример отображает окно, ожидающее пока пользователь не выберет какую-нибудь команду из меню. Если пользователь выберет "run thread", тред
начнет подсчет. Когда он закончит, появится сообщение, информирующее пользователя о том, что работа выполнена. Во время того, как проводится подсчет, пользователь
может выбрать команду "stop thread", чтобы остановить тред.
файл objevent.bas
- Код:
' объект события
#include "windows.bi"
#include "objevent.bi"
declare function WinMain ( byval hInst as HINSTANCE, _
byval hPrevInst as HINSTANCE, _
byval szCmdLine as LPSTR, _
byval iCmdShow as integer ) as integer
declare sub ThreadProc(byval param as uinteger)
' начало программы
dim shared hwnd as HANDLE ' хэндл окна
dim shared hMenu as Handle
dim shared hThread as handle ' хэндл треда
dim shared ThreadID as uInteger ' идентификатор треда
dim shared hEventStart as HANDLE
dim shared EventStop as BOOL = FALSE ' флаг события
end WinMain( GetModuleHandle( NULL ) ,NULL,GetCommandLine() , SW_SHOWNORMAL ) ' вызвать основную функцию
' здесь заканчивается программа
' процедура окна
function WndProc _
(byval hwnd as HWND, _ ' хэндл окна
byval uMsg as UINT, _ ' сообщение
byval wParam as WPARAM, _ ' дополнительный параметр сообщений
byval lParam as LPARAM) as LRESULT ' дополнительный параметр сообщений
function = 0
select case uMsg 'начинаем обработку сообщений
case WM_DESTROY ' если пользователь закрывает окно
PostQuitMessage(0) ' выходим из программы
exit function
case WM_CREATE ' если получили сообщение WM_CREATE
hEventStart = CreateEvent _ ' создадим событие
(NULL, _ ' атрибуты безопасности
FALSE, _ ' автоматически переключить объект события в "выключено"
FALSE, _ ' при создании объекта события, установить его в положение "выключен"
NULL) ' имя объекта события
hThread = CreateThread _ ' создадим тред
(NULL, _ ' атрибуты безопасности
NULL, _ ' размер стека
cPtr(LPTHREAD_START_ROUTINE,@ThreadProc), _ ' указатель на функцию
NULL, _ ' параметр передаваемый функции
0, _ ' дополнительные флаги
@ThreadID) ' указатель на переменную, куда поместится идентификатор треда
CloseHandle _ ' закроем хэндл
(hThread) ' хэндл треда
case WM_COMMAND ' если получили сообщение WM_COMMAND
if lParam = 0 then ' если равно 0, значит идут сообщения от меню
select case loword(wParam) ' начинаем обрабатывать элементы меню
case IDM_START_THREAD ' если в меню выбрали Run thread Thread
SetEvent _ ' установим событие
(hEventStart) ' хэндл события
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_START_THREAD, _ ' идентификатор меню
MF_GRAYED) ' сделаем недоступным
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_STOP_THREAD, _ ' идентификатор меню
MF_ENABLED) ' сделаем доступным
case IDM_STOP_THREAD ' если в меню выбрали stop thread
EventStop = TRUE ' установим флаг события в TRUE
EnableMenuItem _
(hMenu, _ ' хэндл меню
IDM_START_THREAD, _ ' идентификатор меню
MF_ENABLED) ' сделаем доступным
EnableMenuItem _
(hMenu, _ ' хэндл меню
IDM_STOP_THREAD, _ ' идентификатор меню
MF_GRAYED) ' сделаем недоступным
case IDM_EXIT
DestroyWindow(hWnd)
end select
end if
case WM_FINISH ' если сообщение WM_FINISH
MessageBox(NULL,SuccessString,AppName,MB_OK)
end select
function = DefWindowProc(hWnd,uMsg,wParam,lParam) ' Дефаултная функция обработки окна
end function
'функция WinMain
function WinMain _
(byval hInst as HINSTANCE, _ ' хэндл программы
byval hPrevInst as HINSTANCE, _ 'в win32 всегда 0
byval szCmdLine as LPSTR, _ 'указатель на командную строку
byval iCmdShow as integer ) as integer ' состояние окна при первом появлении
dim wc as WNDCLASSEX ' структура параметров окна
dim wMsg as MSG ' структура сообщений
'структура класса окна wc
' заполняем структуру wc
wc.cbSize = SIZEOF( WNDCLASSEX ) ' размер структуры WNDCLASSEX
wc.style = CS_HREDRAW or CS_VREDRAW ' Стиль окна
wc.lpfnWndProc = @WndProc ' Адрес процедуры окна WndProc
wc.cbClsExtra = NULL ' резервирование дополнительных байт за концом структуры
wc.cbWndExtra = NULL
wc.hInstance = hInst ' хэндл модуля
wc.hbrBackground = cast(HGDIOBJ, COLOR_WINDOW+1) ' Цвет фона
wc.lpszMenuName = @MyMenu ' хэндл меню
wc.lpszClassName = @ClassName ' имя класса окна
wc.hIcon = LoadIcon( NULL,IDI_APPLICATION ) ' Хэндл иконки
wc.hIconSm = wc.hIcon 'Хэндл маленькой иконки
wc.hCursor = LoadCursor( NULL,IDC_ARROW) ' Хэндл курсора
' регистрация нашего класса окна
if(RegisterClassEx(@wc) = FALSE) then
MessageBox(0,"Не могу зарегистрировать класс окна","Ошибка",0)
end 1
end if
' Создадим окно
hwnd = CreateWindowEx _
(NULL, _ ' дополнительные стили
ClassName, _ ' строка с именем класса окна
AppName, _ ' строка с именем окна
WS_OVERLAPPEDWINDOW, _ ' стиль окна
CW_USEDEFAULT, _ ' X
CW_USEDEFAULT, _ ' Y
CW_USEDEFAULT, _ ' ширина окна
CW_USEDEFAULT, _ ' высота окна
NULL, _ ' хэндл родительского окна
NULL, _ ' хэндл меню
hInst, _ ' хэндл модуля
NULL) ' указатель на структуру данных
ShowWindow( hwnd,iCmdShow) ' отобразить наше окно на десктопе
UpdateWindow( hwnd) ' обновить клиентскую область
hMenu = GetMenu(hwnd) ' получим хэндл меню
while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE ) 'цикл сообщений
TranslateMessage( @wMsg )
DispatchMessage( @wMsg )
wend
function = wMsg.wParam
end function
sub ThreadProc(byval param as uInteger)
dim in as integer
dim de as integer
do
de=600000000
WaitForSingleObject _ ' ' следим за состоянием объекта события
(hEventStart, _ ' хэндл события
INFINITE) ' наблюдаем бесконечно
while de <> 0 ' пока de неравно 0
if EventStop <> TRUE then ' если флаг неравно TRUE
in +=1
de -=1
in -=3
else
MessageBox(hwnd,@StopString,@AppName,MB_OK) ' выведем строку
EventStop = FALSE ' поставим флаг в FALSE
continue do ' идем к началу петли
end if
wend
postMessage _ ' добавим в очередь сообщений
(hwnd, _ ' хэндл окна
WM_FINISH, _ ' сообщение WM_FINISH
NULL,NULL)
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' хэндл меню
IDM_START_THREAD, _ ' идентификатор меню
MF_ENABLED) ' сделаем доступным
EnableMenuItem _ ' изменим пункт меню
(hMenu, _ ' идентификатор меню
IDM_STOP_THREAD, _ ' идентификатор меню
MF_GRAYED) ' сделаем недоступным
loop
end sub
файл objevent.bi
- Код:
#define ClassName "Win32FreeBasicEventClass" ' класс окна
#define AppName "Win32 FreeBasic Event Example" ' название программы
#define MyMenu "FirstMenu"
#define SuccessString "The calculation is completed!" ' эту строку покажем после завершения расчета
#define StopString "The thread is stopped" ' ну а эту после завершения треда
#define WM_FINISH WM_USER + &h100
#define IDM_START_THREAD 32000
#define IDM_STOP_THREAD 32001
#define IDM_EXIT 32002
файл objevent.rc
- Код:
#include "l16_objevent.bi"
FirstMenu MENU
{
POPUP "&Thread"
{
MENUITEM "&Run thread",IDM_START_THREAD
MENUITEM "&Stop thread",IDM_STOP_THREAD
MENUITEM SEPARATOR
MENUITEM "E&xit",IDM_EXIT
}
}
Анализ:
В этом примере я демонстрирую другую технику работы с тредами.
case WM_CREATE
hEventStart = CreateEvent _
(NULL, _
FALSE, _
FALSE, _
NULL)
hThread = CreateThread _
(NULL, _
NULL, _
cPtr(LPTHREAD_START_ROUTINE,@ThreadProc), _
NULL, _
0, _
@ThreadID)
CloseHandle _
(hThread)
Вы можете видеть, что я создал объект события и тред во время обработки сообщения WM_CREATE. Я создаю объект события, установленного в состояние "выключено" и обладающего свойством автоматического выключения. После того, как объект события создан, я создаю тред. Тем не менее, тред не начинает выполняться немедленно, так как он ждет, пока не включится объект события:
sub ThreadProc(byval param as uInteger)
dim in as integer
dim de as integer
do
de=600000000
WaitForSingleObject _ '
(hEventStart, _
INFINITE)
WaitForSingleObject. ждет, пока не включится объект события, а затем возвращается. Это означает, что даже если тред создан, мы помещаем его в спящее состояние. Когда пользователь выбирает в меню команду "run thread", мы включаем объект события:
case IDM_START_THREAD
SetEvent _
(hEventStart)
Вызов SetEvent включает объект события, после чего WaitForSingleObject возвращается и тред начинает выполняться. Когда пользователь выбирает команду "stop thread", мы устанавливаем значение глобальной переменной в TRUE.
if EventStop <> TRUE then
in +=1
de -=1
in -=3
else
MessageBox(hwnd,@StopString,@AppName,MB_OK)
EventStop = FALSE
continue do
end if
Это останавливает тред и снова передает управление функции WaitForSingleObject. Заметьте, что мы не должны вручную выключать объект, так как мы указали при вызове функции CreateEvent, что значение bManualReset равно FALSE.
[C] Iczelion, пер. Aquila.
electrik- Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 43
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург
Страница 1 из 1
Права доступа к этому форуму:
Вы не можете отвечать на сообщения
|
|