- •Содержание
- •Управление памятью: хорошо, плохо и ужасно
- •Сегментированная память
- •Промежуточные решения
- •И, наконец, 32 бита
- •Выделение памяти
- •Библиотечные функции C
- •Фундаментальное выделение памяти в Windows 95
- •Перемещаемая память
- •Удаляемая память
- •Другие функции и флаги
- •Хорошо ли это?
- •Функции работы с "кучей"
- •Файловый ввод/вывод
- •Старый путь
- •Отличия Windows 95
- •Ввод/вывод с использованием файлов, проецируемых в память
- •Режимы многозадачности
- •Многозадачность в DOS
- •Невытесняющая многозадачность
- •Решения, использующие многопоточность
- •Многопоточная архитектура
- •Коллизии, возникающие при использовании потоков
- •Преимущества Windows
- •Новая программа! Усовершенствованная программа! Многопоточная!
- •Многопоточность в Windows 95
- •И снова случайные прямоугольники
- •Задание на конкурсе программистов
- •Решение с использованием многопоточности
- •О пользе использования функции Sleep
- •Синхронизация потоков
- •Критический раздел
- •Объект Mutex
- •Программа BIGJOB1
- •Объект Event
- •Локальная память потока
- •Печать, буферизация и функции печати
- •Контекст принтера
- •Формирование параметров для функции CreateDC
- •Измененная программа DEVCAPS
- •Вызов функции PrinterProperties
- •Проверка возможности работы с битовыми блоками (BitBlt)
- •Программа FORMFEED
- •Печать графики и текста
- •Каркас программы печати
- •Прерывание печати с помощью процедуры Abort
- •Реализация процедуры прерывания
- •Добавление диалогового окна печати
- •Добавление печати к программе POPPAD
- •Обработка кодов ошибок
- •Техника разбиения на полосы
- •Разбиение на полосы
- •Реализация разбиения страницы на полосы
- •Принтер и шрифты
- •Глава 16 Буфер обмена
- •Простое использование буфера обмена
- •Стандартные форматы данных буфера обмена
- •Передача текста в буфер обмена
- •Получение текста из буфера обмена
- •Открытие и закрытие буфера обмена
- •Использование буфера обмена с битовыми образами
- •Метафайл и картина метафайла
- •Более сложное использование буфера обмена
- •Использование нескольких элементов данных
- •Отложенное исполнение
- •Нестандартные форматы данных
- •Соответствующая программа просмотра буфера обмена
- •Цепочка программ просмотра буфера обмена
- •Функции и сообщения программы просмотра буфера обмена
- •Простая программа просмотра буфера обмена
- •Основные концепции
- •Приложение, раздел и элемент
- •Типы диалогов
- •Символьные строки и атомы
- •Программа сервер DDE
- •Программа DDEPOP1
- •Сообщение WM_DDE_INITIATE
- •Оконная процедура ServerProc
- •Функция PostDataMessage программы DDEPOP1
- •Сообщение WM_DDE_ADVISE
- •Обновление элементов данных
- •Сообщение WM_DDE_UNADVISE
- •Сообщение WM_DDE_TERMINATE
- •Программа-клиент DDE
- •Инициирование диалога DDE
- •Сообщение WM_DDE_DATA
- •Сообщение WM_DDE_TERMINATE
- •Управляющая библиотека DDE
- •Концептуальные различия
- •Реализация DDE с помощью DDEML
- •Элементы MDI
- •Windows 95 и MDI
- •Пример программы
- •Три меню
- •Инициализация программы
- •Создание дочерних окон
- •Дополнительная информация об обработке сообщений в главном окне
- •Дочерние окна документов
- •Освобождение захваченных ресурсов
- •Сила оконной процедуры
- •Основы библиотек
- •Библиотека: одно слово, множество значений
- •Пример простой DLL
- •Разделяемая память в DLL
- •Библиотека STRLIB
- •Точка входа/выхода библиотеки
- •Программа STRPROG
- •Работа программы STRPROG
- •Разделение данных между экземплярами программы STRPROG
- •Некоторые ограничения библиотек
- •Динамическое связывание без импорта
- •Библиотеки, содержащие только ресурсы
- •Глава 20 Что такое OLE?
- •Основы OLE
- •Связь с библиотеками OLE
- •Расшифровка кода результата
- •Интерфейсы модели составного объекта (COM-интерфейсы)
- •Услуги интерфейса IUnknown
- •Является ли OLE спецификацией клиент/сервер?
- •Сервер закрытого компонента
- •IMALLOC.DLL
- •Теперь о макросах
- •Услуги, предоставляемые интерфейсом IUnknown
- •Клиент закрытого компонента
- •Сервер открытого компонента
- •Назначение реестра
- •Способы генерации и использования идентификаторов CLSID
- •Компонент фабрика классов
- •Управление временем жизни сервера
- •Клиент открытого компонента
- •Заключение
47
невозможно. Причина может состоять в том, что потоки имеют возможность захватывать ресурсы, например, память. Если эта память не освобождается при завершении потока, то она продолжает оставаться захваченной. Потоки — не процессы: захваченные ресурсы разделяются между всеми потоками процесса, и поэтому они не освобождаются автоматически при завершении потока. Хороший стиль программирования предписывает, чтобы поток освобождал все захваченные им ресурсы.
Обратите внимание также на то, что третий поток может быть создан, пока второй еще выполняется. Это может произойти, если Windows 95 переключит управление со второго потока на первый между вызовами функций SendMessage и _endthread. В этом случае оконная процедура создаст новый поток в ответ на щелчок мыши. Здесь это не является проблемой, но она может возникнуть в одном из ваших собственных приложений, и тогда вам следует использовать критические разделы для того, чтобы избежать коллизий между потоками.
Объект Event
Программа BIGJOB1 создает поток каждый раз, когда необходимо произвести "дикие" вычисления; поток завершается после выполнения вычислений.
Альтернативным вариантом является сохранение потока готовым к выполнению на протяжении всего времени работы программы, и приведение его в действие только при необходимости. Это идеальный случай для применения объекта Событие (event object).
Объект Событие может быть либо свободным (signaled) или установленным (set), либо занятым (non-signaled) или сброшенным (reset). Вы можете создать объект Событие с помощью функции:
hEvent = CreateEvent(&sa, fManual, fInitial, pszName);
Первый параметр (указатель на структуру типа SECURITY_ATTRIBUTES) и последний параметр (имя объекта событие) имеют смысл только в том случае, когда объект Событие разделяется между процессами. В случае с одним процессом эти параметры обычно имеют значение NULL. Установите значение параметра fInitial равным TRUE, если вы хотите, чтобы объект Событие был изначально свободным, или равным FALSE, чтобы он был занятым. Параметр fManual будет описан несколько позже.
Для того, чтобы сделать свободным существующий объект Событие, вызовите функцию:
SetEvent(hEvent);
Чтобы сделать объект Событие занятым, вызовите функцию:
ResetEvent(hEvent);
Обычно программа вызывает функцию:
WaitForSingleObject(hEvent, dwTimeOut);
где второй параметр имеет значение INFINITE. Возврат из функции происходит немедленно, если объект Событие в настоящее время свободен. В противном случае поток будет приостановлен в функции до тех пор, пока событие не станет свободным. Вы можете установить значение тайм-аута во втором параметре, задав его величину в миллисекундах. Тогда возврат из функции произойдет, когда объект Событие станет свободным или истечет таймаут.
Если параметр fManual при вызове функции CreateEvent имеет значение FALSE, то объект Событие автоматически становится занятым, когда осуществляется возврат из функции WaitForSingleObject. Эта особенность обычно позволяет избежать использования функции ResetEvent.
Рассмотрим теперь программу BIGJOB2, представленную на рис. 14.5.
BIGJOB2.MAK
#-----------------------
# BIGJOB2.MAK make file
#------------- ---------
bigjob2.exe : bigjob2.obj
$(LINKER) $(GUIFLAGS) -OUT:bigjob2.exe bigjob2.obj $(GUILIBS)
bigjob2.obj : bigjob2.c
$(CC) $(CFLAGSMT) bigjob2.c
BIGJOB2.C
/*---------------------------------------- |
|
|
BIGJOB2.C -- |
Multithreading Demo |
|
|
(c) Charles Petzold, |
1996 |
48
---------------------------------------- |
*/ |
#include <windows.h> |
|
#include <math.h> |
|
#include <process.h> |
|
#define REP |
100000 |
#define STATUS_READY |
0 |
#define STATUS_WORKING |
1 |
#define STATUS_DONE |
2 |
#define WM_CALC_DONE |
(WM_USER + 0) |
#define WM_CALC_ABORTED (WM_USER + 1)
typedef struct
{
HWND hwnd;
HANDLE hEvent;
BOOL bContinue;
}
PARAMS, *PPARAMS;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static char szAppName[] = "BigJob2";
HWND |
hwnd; |
MSG |
msg; |
WNDCLASSEX |
wndclass; |
wndclass.cbSize |
= sizeof(wndclass); |
wndclass.style |
= CS_HREDRAW | CS_VREDRAW; |
wndclass.lpfnWndProc |
= WndProc; |
wndclass.cbClsExtra |
= 0; |
wndclass.cbWndExtra |
= 0; |
wndclass.hInstance |
= hInstance; |
wndclass.hIcon |
= LoadIcon(NULL, IDI_APPLICATION); |
wndclass.hCursor |
= LoadCursor(NULL, IDC_ARROW); |
wndclass.hbrBackground |
=(HBRUSH) GetStockObject(WHITE_BRUSH); |
wndclass.lpszMenuName |
= NULL; |
wndclass.lpszClassName |
= szAppName; |
wndclass.hIconSm |
= LoadIcon(NULL, IDI_APPLICATION); |
RegisterClassEx(&wndclass);
hwnd = CreateWindow(szAppName, "Multithreading Demo", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void Thread(PVOID pvoid)
{
49
double A = 1.0; INT i;
LONG lTime;
PPARAMS pparams;
pparams =(PPARAMS) pvoid;
while(TRUE)
{
WaitForSingleObject(pparams->hEvent, INFINITE);
lTime = GetCurrentTime();
for(i = 0; i < REP && pparams->bContinue; i++) A = tan(atan(exp(log(sqrt(A * A))))) + 1.0;
if(i == REP)
{
lTime = GetCurrentTime() - lTime;
SendMessage(pparams->hwnd, WM_CALC_DONE, 0, lTime);
}
else
SendMessage(pparams->hwnd, WM_CALC_ABORTED, 0, 0);
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
static char
static HANDLE |
hEvent; |
static INT |
iStatus; |
static LONG |
lTime; |
static PARAMS |
params; |
char |
szBuffer[64]; |
HDC |
hdc; |
PAINTSTRUCT |
ps; |
RECT |
rect; |
switch(iMsg)
{
case WM_CREATE :
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
params.hwnd = hwnd; params.hEvent = hEvent; params.bContinue = FALSE;
_beginthread(Thread, 0, ¶ms);
return 0;
case WM_LBUTTONDOWN :
if(iStatus == STATUS_WORKING)
{
MessageBeep(0); return 0;
}
iStatus = STATUS_WORKING;
params.bContinue = TRUE;
SetEvent(hEvent);