Многопотоковые программы




 

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

Важно понять, что все процессы имеют как минимум один поток выполнения. Для удобства изложения он будет называться главным потоком. Однако можно создать более чем один поток выполнения в одном процессе. Вновь созданный поток сразу же начинает выполняться параллельно с главным. Таким образом, каждый процесс начинает выполняться в одном (главном) потоке и по ходу выполнения может создавать один или более дополнительных потоков. Это и есть способ написания многопотоковых программ в Windows.


В этом разделе мы рассмотрим, как разрабатываются многопотоковые программы.

 

Создание потока. Для создания потока используется функция API CreateThread(). Вот прототип этой функции:

 

HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpAttr,

DWORDdwStack,

LPTHREAD_START_ROUTINE lpFunc,

LPVOID lpParam, DWORD dwFlags,

LPDWORD lpdwID);

 

Здесь параметр lpAttr является указателем на структуру атрибутов доступа, предо­ставляемых создаваемому процессу. В Windows XP (а также NT или 2000), этот указатель может быть равен NULL.

Каждый поток имеет свой собственный стек. Размер стека для создаваемого потока в байтах можно определить в параметре dwStack. Если же значение этого параметра равно 0, стек создаваемого потока имеет такой же размер, как и стек родительского потока; при необходимости он будет расширяться. Поэтому чаще всего в качестве значения этого параметра задают 0.

Выполнение каждого потока начинается с вызова функции, называемой потоко­вой функцией, и продолжается до завершения этой функции. Адрес потоковой функции (точки входа для потока) задается в параметре lpFunc. Все потоковые функции должны иметь следующий прототип:

 

DWORD threadfunc(LPVOID param);

 

Любой аргумент, который Вы хотите задать для нового потока, определяется при создании потока в параметре lpParam функции CreateThread(). Это 32-разрядное значение передается в качестве параметра в потоковую функцию и может быть использовано для любых целей.

Параметр dwFlags определяет состояние потока. Если его значение равно 0, выпол­нение потока начинается немедленно. Если это значение равно CREATE_SUSPEND, то поток будет находиться в состоянии ожидания до поступления сигнала, разрешающего начало выполнения. (Разрешить выполнение потока в этом случае можно при помощи функции ResumeThread().) Идентификатор созданного потока записывается в двойное слово, адрес которого передается в параметре lpdwID.

Функция возвращает дескриптор созданного потока при успешном завершении либо нуль при возникновении ошибки.

Завершение потока. Как уже было сказано, поток завершается при возвращении из потоковой функции. Кроме того, родительский процесс может принудительно завершить его при помощи вызова функции TerminateThread():

 

BOOL TerminateThread(HANDLE hThread, DWORD dwStatus);

 

Параметр hThread содержит дескриптор потока, который должен быть завершен, a dwStatus определяет код завершения. Функция возвращает ненулевое значение при успешном завершении и нуль при возникновении ошибки.

Поток, завершаемый при помощи вызова функции TerminateThread(), прекра­щает выполнение немедленно и не выполняет никаких действий по освобождению памяти, ресурсов и т.п. Кроме того, функция TerminateThread() может завершить поток, выполняющий в это время какую-нибудь важную операцию. Поэтому обычно лучшим (и простейшим) способом завершения потока является завершение потоко­вой функции. Именно этот подход используется в примерах данной главы.

 

Пример 12-2. Многопотоковая программа

 

Приведенная ниже программа создает новый поток каждый раз при поступлении команды меню Новый поток. Этот поток 10 раз подает звуковой сигнал и при этом выводит на экран его номер вместе с идентификатором потока. Другой поток может быть создан до того, как завершится поток, созданный по предыдущей команде.

Звуковой сигнал генерируется при вызове функции API Beep(). Эта функция имеет следующий вид:

 

BOOL Beep(DWORD frequency, DWORD duration);

 

Параметр frequency – это высота (тональность) звука в герцах, а duration – продолжительность звучания в миллисекундах. Например, если нужно получить сигнал с тональностью звука 600 герц и продолжительностью 0,25 секунды, нужно выполнить функцию

 

Beep(600, 250); // 600 Гц, 0,25 сек

 

В качестве основы для этой программы используется текст, рассмотренный в примере 12-1. Также применяется техно­логия виртуального окна для отображения и обновления содержимого реального окна.

 

// Простая многопотоковая программа,

// с использованием виртуального окна

#include <Windows.h>

#include <String.h>

#include <Stdio.h>

#include "Proc.h"

#define Procmax 5

 

LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);

DWORD MyThread(LPVOID); // Функция потока

char szWinName[] = "МоеОкно"; // Имя класса окна

char str[255]; // Буфер строки вывода

int X=0, Y=0; // Текущие координаты строки

int procnum=0; // Количество активных процессов

DWORD Tid; // Идентификатор потока

int maxX, maxY; // Размеры экрана

HDC memdc; // DC виртуального окна

HBITMAP hbit; // Растр - это виртуальное окно

HBRUSH hbrush; // Дескриптор кисти

 

PROCESS_INFORMATION pinfo[Procmax];

 

int WINAPI WinMain (HINSTANCE hThisInst,

HINSTANCE hPrevInst,

LPSTR lpszArgs,

int nWinMode)

{

HWND hwnd;

MSG msg;

WNDCLASS wcl;

HACCEL hAccel;

// Определить класс окна

wcl.hInstance=hThisInst; // Дескриптор приложения

wcl.lpszClassName=szWinName; // Имя класса окна

wcl.lpfnWndProc=WindowFunc; // Функция окна

wcl.style=0; // Стиль по умолчанию

wcl.hIcon=LoadIcon(NULL,IDI_APPLICATION); // Иконка

wcl.hCursor=LoadCursor(NULL,IDC_ARROW); // Курсор

wcl.lpszMenuName="MYMENU"; // Меню

wcl.cbClsExtra=0; // Без дополнительной

wcl.cbWndExtra=0; // информации

// Определить заполнение окна белым цветом

wcl.hbrBackground=

(HBRUSH)GetStockObject(WHITE_BRUSH);

if(!RegisterClass(&wcl)) // Зарегистр. класс окна

return 0;

// Создать окно

hwnd=CreateWindow(szWinName, // Имя класса

"Порождение процессов и потоков",

WS_OVERLAPPEDWINDOW,// Стиль окна

CW_USEDEFAULT, // Х-координата

CW_USEDEFAULT, // Y-координата

CW_USEDEFAULT, // Ширина окна

CW_USEDEFAULT, // Высота окна

HWND_DESKTOP, // Нет родит. окна

NULL, // Нет меню

hThisInst, // Дескрип. приложения

NULL); // Без дополит. аргументов

// Загрузить акселераторы

hAccel=LoadAccelerators(hThisInst,"MYMENU");

ShowWindow(hwnd,nWinMode); // Показать окно и

UpdateWindow(hwnd); // перерисовать содержимое

// Запустить цикл обработки сообщений

while(GetMessage (&msg,NULL,0,0))

if(!TranslateAccelerator(hwnd,hAccel,&msg))

{

TranslateMessage(&msg); // Использ.Клавиатуры

DispatchMessage (&msg); // Возврат к Windows

}

return msg. wParam;

}

 

// Следующая функция вызывается операционной системой

// Windows и получает в качестве параметров сообщения

// из очереди сообщений данного приложения

 

LRESULT CALLBACK WindowFunc(HWND hwnd,

UINT message,

WPARAM wParam,

LPARAM lParam)

{

HDC hdc;

PAINTSTRUCT paintstruct;

TEXTMETRIC tm;

STARTUPINFO startin;

switch(message)

{

case WM_CREATE: // Получаем размеры экрана

maxX=GetSystemMetrics(SM_CXSCREEN);

maxY=GetSystemMetrics(SM_CYSCREEN);

hdc=GetDC(hwnd); // Совмест. с окном растр

memdc=CreateCompatibleDC(hdc);

hbit=CreateCompatibleBitmap(hdc,maxX,maxY);

SelectObject(memdc,hbit);

hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH);

SelectObject(memdc,hbrush);

PatBlt(memdc,0,0,maxX,maxY,PATCOPY);

ReleaseDC(hwnd,hdc);

break;

case WM_COMMAND:

switch(LOWORD(wParam))

{

case ID_PROCESS:

if(procnum == Procmax)

{

MessageBox(hwnd,

"Нельзя создать больше",

"Ошибка", MB_OK);

break;

} // Не более чем Procmax

// Получить метрики текста */

GetTextMetrics(memdc, &tm);

sprintf(str, "Порождается процесс %d",

procnum);

TextOut(memdc, X, Y, str, strlen(str));

Y = Y+tm.tmHeight+tm.tmExternalLeading;

InvalidateRect(hwnd,NULL,1); //Сообщить

// Порождение нового процесса

startin.cb=sizeof(STARTUPINFO);

startin.lpReserved = NULL;

startin.lpDesktop = NULL;

startin.lpTitle = NULL;

startin.dwFlags =STARTF_USESHOWWINDOW;

startin.cbReserved2 = 0;

startin.lpReserved2 = NULL;

startin.wShowWindow = SW_SHOWMINIMIZED;

CreateProcess(NULL, "Test.exe", NULL,

NULL, FALSE, 0, NULL,

NULL, &startin,

&(pinfo[procnum]));

procnum++;

break;

case ID_KILLPROC:

if(procnum)

procnum--;

else

{

MessageBox(hwnd,

"Больше процессов нет",

"Ошибка", MB_OK);

break;

}

// Получить метрики текста

GetTextMetrics(memdc, &tm);

TerminateProcess

(pinfo[procnum].hProcess, 0);

sprintf(str, "Процесс %d завершен",

procnum);

TextOut(memdc, X, Y, str, strlen(str));

Y = Y+tm.tmHeight+tm.tmExternalLeading;

InvalidateRect(hwnd,NULL,1); //Сообщить

break;

case ID_THREAD:

CreateThread

(NULL, 0,

(LPTHREAD_START_ROUTINE)MyThread,

(LPVOID)NULL, 0, &Tid);

InvalidateRect (hwnd, NULL, 1);

break;

case ID_HELP:

MessageBox(hwnd,

"F2: Новый процесс\n"

"F3: Завершить процесс\n"

"F4: Новый поток",

"Помощь:", MB_OK);

InvalidateRect(hwnd,NULL,1); //Сообщить

break;

}

break;

case WM_PAINT: // Перерисовка окна

hdc=BeginPaint(hwnd,&paintstruct); // Пол. DC

// Теперь копируем растр из памяти на экран

BitBlt(hdc,0,0,maxX,maxY,memdc,0,0,SRCCOPY);

EndPaint(hwnd,&paintstruct); // Освободить DC

break;

case WM_DESTROY: // Завершение программы

DeleteDC(memdc); // Удалить виртуальное окно

PostQuitMessage(0);

break;

default:

// Все сообщения, не обрабатываемые в данной

// функции, направляются на обработку по

// умолчанию

return DefWindowProc(hwnd,message,

wParam,lParam);

}

return 0;

}

 

//

// Начало выполнения нового потока

//

 

DWORD MyThread(LPVOID param)

{

int i;

DWORD curTid = Tid;

TEXTMETRIC tm;

// Получить метрики текста

GetTextMetrics(memdc, &tm);

for(i=0; i<10; i++)

{

Sleep(500); // Задержка 0,5 сек

sprintf(str, "Поток %ld, сигнал %d", curTid, i);

TextOut(memdc, X, Y, str, strlen(str));

Y = Y + tm.tmHeight + tm.tmExternalLeading;

InvalidateRect((HWND)param, NULL, 1);

Beep(600, 250); // 600 Гц, 0,25 сек

}

return 0;

}

 

 

Результаты работы этой многопотоковой программы приведены на рисунке 12.2.

Рис. 12.2. Пример окна многопотоковой программы

 


Для работы программы потребуется файл ресурсов Process.rc из предыдущего примера, который дополнен кнопкой меню Новый поток и соответствующим акселератором:

 

#include <Windows.h>

#include "Proc.h"

MYMENU MENU

{

POPUP "Процессы"

{

MENUITEM "Новый процесс", ID_PROCESS

MENUITEM "Завершить процесс", ID_KILLPROC

MENUITEM "Новый поток", ID_THREAD

}

MENUITEM "Помощь", ID_HELP

}

MYMENU ACCELERATORS

{

VK_F2, ID_PROCESS, VIRTKEY

VK_F3, ID_KILLPROC, VIRTKEY

VK_F4, ID_THREAD, VIRTKEY

VK_F1, ID_HELP, VIRTKEY

}

 

Соответственно, используется тот же файл Proc.h, дополненный новой константой:

 

#define ID_PROCESS 100

#define ID_KILLPROC 101

#define ID_THREAD 102

#define ID_HELP 103

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-03-31 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: