Главная arrow С++ (часть 3) arrow функция ThreadPanelO

функция ThreadPanelO

функция ThreadPanel () — это Windows-функция обратного вызова (callback function), которая реагирует на взаимодействие пользователя с панелью управления. Как все функции обратного вызова диалогового окна, она получает сообщение каждый раз, когда пользователь изменяет состояние элемента управления. Ей передается дескриптор диалогового окна, в котором произведено действие, сообщение и дополнительная информация, требуемая сообщением. Основной режим функционирования ThreadPanel () такой же, как у любой функции обратного вызова, используемой диалоговым окном. Дальнейшее обсуждение посвящено описанию того, что происходит при получении каждого сообщения.
Когда диалоговое окно панели управления потоком создается впервые, она получает сообщение WM_INITDIALOG, которое обрабатывается следующим case-оператором.
case WM_INITDIALOG:
// Инициализирует список значений приоритета. for(i=0;  i<NUMPRIORITIES; i++)  { SendDlgltemMessage(hwnd, IDD_LB,
LB_ADDSTRING.  0,   (LPARAM) priorities[i]);
}
// Устанавливает кнопки приостановки и возобновления потока. hpbSus = GetDlgItem(hwnd, IDD_SUSPEND); hpbRes = GetDlgltem(hwnd,  IDD_RESUME);
EnableWindow(hpbSus, true); // делает доступной кнопку Suspend EnableWindow(hpbRes, false); // делает недоступной Resume return 1;
Она инициализирует список приоритетов и устанавливает кнопки Suspend и *ksume в начальное состояние: первая из них доступна, вторая — нет.
Любое взаимодействие пользователя с панелью управления вызывает генерацию сообщения WM_COMMAND. При получении такого сообщения извлекает-Ся итератор, указывающий на элемент в переменной dialogmap.
Case WM_COMMAND:
map<HWND, Threadlnfo>::iterator p = dialogmap.find(hwnd);
Информация, на которую указывает извлеченный итератор р, будет использоваться для конкретной обработки каждого действия. Поскольку р — итератор отображения, он указывает на объект типа pair, представляющий собой структуру, определенную в библиотеке STL. Эта структура содержит два поля: first и second, соответствующих двум типам данных: ключу и значению В данном случае дескриптор играет роль ключа, а информация о потоке — роль значения.
Код, точно описывающий совершенное действие, хранится в младшем слове параметра wParam и используется для управления выполнением оператора switch, который обрабатывает оставшиеся типы сообщений. Далее описан каждый из них.
Когда пользователь нажимает кнопку Terminate, управляемый поток завершает выполнение, этот тип сообщения обрабатывается приведенным далее case-оператором.
case IDD_TERMINATE:
TerminateThread(p->second.hThread, 0);
// Делает кнопку Terminate недоступной. hpbTerm = GetDlgItem(hwnd, IDD_TERMINATE);  } EnableWindow(hpbTerm, false); //не доступна
// Делает кнопки Suspend и Resume недоступными. hpbSus = GetDlgltem(hwnd, IDD_SUSPEND); hpbRes = GetDlgltem(hwnd, IDD_RESUME); EnableWindow(hpbSus. false); //не доступна Suspend EnableWindow(hpbRes, false); //не доступна Resume return 1;
Поток останавливается вызовом функции TerminateThreadO. Обратите внимание на способ получения дескриптора потока. Как уже объяснялось, поскольку р — итератор отображения (тар), он указывает на объект типа pair, содержащий ключ в поле first и значение в поле second. Таким образом, дескриптор потока извлекается с помощью выражения p->second.hThread. После остановки потока кнопка Terminate становится недоступной. Остановленный поток нельзя возобновить. Панель управления для завершения выполнения потока использует функцию TerminateThreadO. Как отмечалось ранее, ее надо применять с осторожностью. Если вы используете панель управления для экспериментов с вашими собственными потоками, нужно быть уверенными в отсутствии возможных вредных побочных влияний-Если пользователь нажимает кнопку Suspend, поток приостанавливается-Это действие реализовано приведенной далее последовательностью кода.
case IDD_SUSPEND:
SuspendThread(p->second.hThread);
// Устанавливает состояние кнопок Suspend и Resume. hpbSus = GetDlgItem(hwnd, IDD_SUSPEND); hpbRes = GetDlgItem(hwnd, IDD_RESUME);
EnableWindow(hpbSus, false); // делает недоступной Suspend EnableWindow(hpbRes, true);    // делает доступной Resume
p->second.suspended = true; return 1;
Для временной остановки потока вызывается функция SuspendThreado. Далее обновляются состояния кнопок: кнопка Resume становится доступной, а кнопка Suspend — недоступной. Такое состояние кнопок защищает пользователя от попытки приостановить поток дважды.
Приостановленный поток возобновляется после нажатия кнопки Resume, как показано в приведенном далее фрагменте кода.
case IDD_RESUME:
ResumeThread(p->second.hThread);
// Устанавливает состояние кнопок Suspend и Resume. hpbSus = GetDlgltem(hwnd, IDD_SUSPEND); hpbRes = GetDlgltem(hwnd,  IDD_RESUME);
EnableWindow(hpbSus, true);    // делает доступной Suspend EnableWindow(hpbRes, false); //не доступна Resume
p->second.suspended = false; return 1;
Выполнение потока возобновляется функцией ResumeThread о, а кнопки Suspend и Resume соответствующим образом меняют свое состояние. Для изменения значения приоритета пользователь дважды щелкает кнопкой мыши по нужному элементу списка priority. Обработка этого события Приведена далее.
case IDD_LB:
// Если выбран элемент списка, // изменяет приоритет потока, if(HIWORD(wParam)==LBN_DBLCLK)  {
p->second.priority = SendDlgltemMessage(hwnd,
IDD_LB, LB_GETCURSEL, О,  О);
SetThreadPriority(p->second.hThread,
p->second.priori ty-OFFSET);
}
return 1;
Обработка списков порождает разнообразные уведомляющие сообщения (notification message), точно описывающие тип происшедшего события. Такие сообщения хранятся в старшем слове параметра wParam. Один из типов уведомляющих сообщений носит имя LBN_DBLCLK, которое означает двойной щелчок кнопкой мыши по элементу списка. Когда такое уведомление получено, извлекается индекс элемента списка с помощью Windows API-функции sendDigitemMessageo, использующей текущее выделение в списке. Полученное значение индекса применяется для установки нового приоритета потока. Обратите внимание на то, что переменная OFFSET вычитается для нормализации извлеченного значения индекса.
В конце концов, когда пользователь закрывает панель управления, посылается сообщение IDCANCEL. ОНО обрабатывается приведенной далее последовательностью кода.
case IDCANCEL:
// Если поток приостановлен, когда панель закрывается,
// возобновляет поток, чтобы предотвратить возникновение тупика.
i f(p->second.suspended) {
ResumeThread(p->second.hThread); p->second.suspended = false;
}
// Удаляет этот поток из списка, dialogmap.erase(hwnd);
// Закрывает панель. DestroyWindow(hwnd); return 1;
Если поток был приостановлен, он возобновляется. Это необходимо для исключения возможной тупиковой ситуации или взаимной блокировки потоков. Далее удаляется связанный с этой панелью элемент в отображении dialogmap. В заключение диалоговое окно закрывается с помощью Windows API-функции DestroyWindow ().
демонстрация работы панели управления потоком
В листинге 3.2 приведен код программы, включающей в себя панель управления и пример ее использования. Пример вывода программы показан на рис. 3.2. Программа создает главное окно и определяет два дочерних потока. После запуска программы в этих потоках происходит подсчет суммы целых чисел от 0 до 50 ООО и вывод текущих значений счетчиков в главном окне. Управлять дочерними потоками можно, активизируя их панели управления.
Начните выполнение программы с запуска потоков с помощью команды Threads | Start Threads (Потоки | Запуск потока) (или нажатием клавиши <F2>) и активизации панелей управления потоками с помощью команды Threads | Control Panels (Потоки | Панели управления) (или нажатием клавиши <F3>). После того как панели запущены, вы можете менять приоритеты потоков и т. д.
 
^    Примечание    ^
В задачу этой книги не входит обучение программированию в операционной системе Windows, но функционирование предлагаемой демонстрационной программы настолько очевидно, что будет понятно всем программистам, работающим в этой операционной системе.
Листинг 3.2. Демонстрация работы панели управления потоком
#include <windows.h> #include <process.h> #include "thrdapp.h" #include "tcp.cpp"
const int MAX = 500000;
LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM,  LPARAM);
unsigned _stdcall MyThreadl(void * param);
unsigned _stdcall MyThread2 (void * param) ;
char str[255]; // содержит строки вывода
unsigned tidl, tid2; //. идентификаторы потоков (ID) HANDLE hThreadl, hThread2; // дескрипторы потоков
HINSTANCE hlnst; // дескриптор экземпляра приложения int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR args, int winMode)
{
HWND hwnd; MSG msg; WNDCLASSEX wcl; HACCEL hAccel;
// Определяет оконный класс (window class). wcl.cbSize = sizeof(WNDCLASSEX);
wcl.hlnstance = hThisInst;        // дескриптор для этого экземпляра wcl.IpszClassName = "MyWin";    // имя класса окна wcl.lpfnWndProc = WindowFunc; // функция окна (window function) wcl.style =0; // текущий стиль
wcl.hlcon = Loadlcon(NULL,  IDI_APPLICATION); // большая пиктограмма wcl.hlconSm = NULL; // использует уменьшенную версию // большой пиктограммы
^cl.hCursor = LoadCursor(NULL, IDC_ARROW); // стиль курсора ^cl.lpszMenuName = "ThreadAppMenu"; // главное меню
wcl.cbClsExtra =0; // дополнительной памяти не требуется ^cl.cbWndExtra = 0;
// Делает цвет фона окна белым.
wcl-hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
// регистрирует класс окна.
if (!RegisterClassEx(&wcl)) return 0;
/* Теперь, когда класс окна зарегистрирован,
можно создавать окно. */
hwnd = CreateWindow(
wcl. IpszClassName, // имя класса окна
"Using a Thread Control Panel", // заголовок
WS_OVERLAPPEDWINDOW, // стиль окна - normal
CWJCJSEDEFAULT,  // координата X - пусть решает Windows
CWJUSEDEFAULT,  // координата Y - пусть решает Windows
260, // ширина
200, // высота
NULL, // нет родительского окна
NULL, // нет переопределения меню класса
hThisInst, // дескриптор экземпляра
NULL // нет дополнительных аргументов
);
hlnst = hThisInst; // сохраняет дескриптор экземпляра II Загружает клавиши быстрого вызова.
hAccel = LoadAccelerators(hThisInst,  "ThreadAppMenu");
// Отображает окно. SftowWindow (hwnd, winMode) ; °PdateWindow (hwnd) ;
11 Создает цикл обработки сообщения. while(GetMessage(&msg, NULL, 0, 0))
{
i f(!TranslateAccelerator(hwnd, hAccel, &msg)) {
TranslateMessage(&msg); // трансляция клавиатурных сообщений DispatchMessage(&msg); // возврат управления Windows
}
}
return msg.wParam;
}
/* Эта функция вызывается Windows и ей передаются сообщения из очереди сообщений.
*/
LRESULT CALLBACK WindowFunc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM 1 Param)
{
int response;
switch (message)  { case WM_COMMAND:
switch (LOWORD (wParam)) {
case IDM_THREAD: // создает потоки
hThreadl = (HANDLE) _beginthreadex(NULL, 0,
MyThreadl,  (void *) hwnd, 0, &tidl);
hThread2 = (HANDLE) _beginthreadex(NULL, 0,
MyThread2,  (void *) hwnd, 0, &tid2);
break;
case IDM_PANEL: // активизирует панель управления
ThrdCtrlPanel(hlnst, hThreadl);
ThrdCtrlPanel(hlnst, hThread2);
break; case IDM_EXIT:
response = MessageBox(hwnd, "Quit the Program?", "Exit", MB_YESNO);
if(response == IDYES) PostQuitMessage(O);
break; case IDM_HELP:
MessageBox(hwnd, ~
"Fl: Help\nF2: Start Threads\nF3: Panel", "Help", MB_OK);
break;
}
break;
case WM_DESTROY: // завершает программу
PostQuitMessage(O);
break; default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
// Первый поток.
unsigned _stdcall MyThreadl (void * param)
{
int i; HDC hdc;
for(i=0; i<MAX; i++)  { wsprintf(str,  "Thread 1: loop # %5d ", i); hdc = GetDC((HWND) param); TextOut(hdc, 1, 1, str, lstrlen(str)); ReleaseDC ((HWND) param, hdc) ;
}
return 0;
).
11 Второй поток.
^signed _stdcall MyThread2 (void * param)
{
int i;
HDC hdc;
Јor(i=0;  i<MAX;  i++)   { wsprintf(str,  "Thread 2: loop # %5d ", i); hdc = GetDC((HWND) param) ; TextOut(hdc, 1, 20, str, lstrlen(str)); ReleaseDC((HWND) param, hdc);
}
return 0; }
Программа (см. листинг 3.2) требует включения заголовочного файла thrdapp.h, содержимое которого приведено далее.
#define IDM_THREAD 100
#define IDM_HELP 101
#define IDM_PANEL 102
#define IDM_EXIT 103
Кроме того, ей нужен следующий файл ресурса: #include <windows.h> #include "thrdapp.h" #include "tcp.rc"
ThreadAppMenu MENU {
POPUP "^Threads" {
MENUITEM "&Start Threads\tF2", IDM_THREAD MENUITEM "ScControl Panels\tF3", IDM_PANEL MENUITEM "E&xit\tCtrl+X",  IDM_EXIT
}
MENUITEM "&Help",  IDM_HELP
}
ThreadAppMenu ACCELERATORS {
VK_F1,  IDM_HELP, VIRTKEY VK_F2,  IDM_THREAD, VIRTKEY VK_F3,  IDM_PANEL, VIRTKEY "ЛХ",  IDM_EXIT
}