
- •1. Синхронизирующие объекты
- •1.1. Критические разделы
- •Синхронизация потоков с объектами ядра
- •2.1. Объекты mutex
- •2.2.Семафоры
- •2.3.События
- •2.4.События со сбросом вручную
- •2.5.События с автоматическим сбросом
- •InitializeCriticalSection(&g_CriticalSection);
- •0, &DwThreadId);
- •0, &DwThreadId);
- •Void Dlg_OnDestroy (hwnd hwnd) {
- •Void Dlg_OnCommand (hwnd hwnd, int id, hwnd hwndCtl,
- •If (Button_GetCheck(hwndCtl)) {
- •Int winapi WinMain (hinstance hinstExe,
2.5.События с автоматическим сбросом
Объекты "события с автоматическим сбросом" больше похожи на семафоры и объекты mutex, чем на события со сбросом вручную. Когда поток вызывает SetEvent, чтобы освободить событие, оно остается в таком состоянии, пока не пробудится другой поток, ожидающий тот же объект. За мгновение до возобновления работы потока система автоматически переводит событие в занятое состояние. Применение объекта "событие с автоматическим сбросом" позволяет возобновить исполнение лишь какого-то одного из ожидающих потоков. Прочие потоки по-прежнему "спят" и ждут. Решение о том, какой именно из ждущих потоков возобновит исполнение, система принимает самостоятельно. Это относится не только к событиям, но и ко всем синхронизирующим объектам. Но если у ждущих потоков разный приоритет, первым возобновит исполнение тот, у кого он наивысший.
Этим типом событий, как и событиями со сбросом вручную, управляют функции SetEvent, ResetEvent и PulseEvent. ResetEven обычно не используют, т. к. система перед возвратом из WaitForSingleObject и WaitForMultipleObjects автоматически сбрасывает (переводит в занятое состояние) эти объекты.
Функция PulseEvent выполняет над событиями с автоматическим сбросом те же операции, что и над событиями со сбросом вручную: освобождает событие, разрешает исполнение ждущему этот объект потоку и вновь переводит объект в занятое состояние. Есть небольшое отличие в том, что происходит при вызове данной функции применительно к событиям с автоматическим сбросом: возобновляется исполнение лишь одного из ожидающих событие потоков, тогда как в случае событий со сбросом вручную исполнение могли продолжить все потоки, его ожидающие.
3. СОДЕРЖАНИЕ ОТЧЕТА
Постановка задачи.
Состав и структура программного модуля.
Блок-схема алгоритма.
Листинг программы.
Протокол диалога и выходные данные программы.
Анализ полученных результатов.
ЛИТЕРАТУРА
Кинг А. Windows 95 изнутри/Перев. с англ. - СПб: Питер, 1995. - 512 с.
Мэтт П. Секреты системного программирования в Windows 95.- Киев: издательство "Диалект", 1996. - 448 с.
Рихтер Д. Windows для профессионалов (программирование в Win32 API для Windows NT 3.5и Windows 95).Пер. с англ. - М.: Издательский отдел "Русская редакция" ТОО "Channel Trading Ltd.",1995. - 720 с.
ПРИЛОЖЕНИЕ
/*****************************************************************
Модуль: CritSecs.C
*****************************************************************/
#include "..\afxres.h"
#include <windows.h>
#include <windowsx.h>
#include "..\AdvWin32.H"
#pragma warning(disable: 4001) /* Одностроковый комментарий */
#include <tchar.h>
#include <stdio.h> // для sprintf
#include <process.h> // для _beginthreadex
//////////////////////////////////////////////////////////////////
// Глобальные переменные
HWND g_hwndDlg = NULL;
HANDLE g_hThreadCntr = NULL;
HANDLE g_hThreadDspy = NULL;
// Данные, подлежащие защите
int g_szNumber = 0;
// Критический раздел, используемый для защиты этих данных
CRITICAL_SECTION g_CriticalSection;
//////////////////////////////////////////////////////////////////
// Добавление строки в окно списка
void AddToListBox(LPCTSTR szBuffer) {
HWND hwndDataBox = GetDlgItem(g_hwndDlg, IDC_DATABOX);
int x = ListBox_AddString(hwndDataBox, szBuffer);
ListBox_SetCurSel(hwndDataBox, x);
if (ListBox_GetCount(hwndDataBox) > 100)
ListBox_DeleteString(hwndDataBox, 0);
}
//////////////////////////////////////////////////////////////////
// Поток, который увеличивает счетчик защищенных данных
void CounterThread (LPVOID lpThreadParameter) {
unsigned int nNumber, nDigit;
BOOL fSyncChecked;
for (;;) {
// Узнаем состояние флажка Synchronize и запоминаем его
fSyncChecked =
IsDlgButtonChecked(g_hwndDlg, IDC_SYNCHRONIZE);
if (fSyncChecked) {
// Если пользователь хочет синхронизировать потоки
EnterCriticalSection(&g_CriticalSection);
}
// Преобразуем символьное представление числа в целое значение и добавляем 1
_stscanf(g_szNumber, __TEXT("%d"), &nNumber);
g_szNumber++;
// Преобразуем новое число в строку
/* */ nDigit = 0;
while (nNumber != 0) {
// Помещаем разряд числа в строку
g_szNumber[nDigit++] = (TCHAR)
(__TEXT("0") + (nNumber % 10));
// Вызов здесь функции Sleep сообщает системе, что неиспользованный остаток текущего
// кванта времени нужно отдать другому потоку. Этот вызов необходим в однопроцессорной
// системе для того, чтобы результат синхронизации потоков или отсутствия таковой стали
// очевидны. Обычно в программах функцию Sleep для этого не вызывают.
Sleep(0);
// Готовимся получить следующий разряд
nNumber /= 10;
}
// Все разряды преобразованы в строку.
// Завершаем строку.
g_szNumber[nDigit] = 0;
// Символы сформированы в обратном порядке: восстанавливаем нормальный порядок.
// Если ANSI, вызываем strrev, а если Unicode. - _wcsrev.
_tcsrev(g_szNumber);
/* */
if (fSyncChecked) {
// Если пользователь хочет синхронизировать потоки.
LeaveCriticalSection(&g_CriticalSection);
}
// Если пользователь - после каждой итерации хочет видеть на экране значение Cntr:
if (IsDlgButtonChecked(g_hwndDlg, IDC_SHOWCNTRTHRD))
AddToListBox(__TEXT("Cntr: Increment"));
}
return; //
//////////////////////////////////////////////////////////////////
// Поток, который добавляет текущее значение счетчика (собственно данные) в окно списка
void DisplayThread (LPVOID lpThreadParameter) {
BOOL fSyncChecked;
TCHAR szBuffer[50];
for (;; ){
// Определяем: нужна ли синхронизация потоков
fSyncChecked = IsDlgButtonChecked(g_hwndDlg, IDC_SYNCHRONIZE);
if (fSyncChecked)
EnterCriticalSection(&g_CriticalSection);
// Формируем строку с символьным представлением числа
_stprintf(szBuffer, __TEXT("Dspy: %d"), g_szNumber);
if (fSyncChecked)
LeaveCriticalSection(&g_CriticalSection);
// Добавляем строку в окно списка
AddToListBox(szBuffer);
}
return;
}
//////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog (HWND hwnd, HWND hwndFocus,
LPARAM lParam) {
HWND hWndCtl;
DWORD dwThreadID;
// Запоминание описателя диалогового окна в глобальной переменной, чтобы потоки могли
// легко получить к ней доступ. (Это необходимо сделать до создания потоков.)
g_hwndDlg = hwnd;
// Связываем значок с диалоговым окном
SetClassLong(hwnd, GCL_HICON, (LONG)
LoadIcon((HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE),
__TEXT("CritSecs")));
// Инициализация критического раздела. Это нужно сделать до того, как к нему попытается
// обратиться какой-нибудь поток.