21 Потоковая многозадачность (1061100)
Текст из файла
11 стр. Лекция 21. Потоковая многозадачность
Потоковая многозадачность
Потоковая многозадачность - это одно из важных улучшений Windows, которое служит причиной существенного повышения производительности системы. В современных версиях Windows поддерживается два типа многозадачности.
Первый тип основан на процессах. Такая многозадачность поддерживалась уже с первых версий Windows. Процесс - это программа, или задача, которая выполняется. В многозадачных системах такого типа две и более программы могут выполняться одновременно.
Второй тип многозадачности основан на потоках. Такая многозадачность поддерживается оболочкой Win32 и используется в Windows 95 и Windows NT. Поток - это часть выполняющегося процесса. В Windows 95/NT каждый процесс имеет по крайней мере один поток, но потоков процесса может быть и два, и больше.
В потоковой многозадачности несколько частей одной и той же программы могут выполняться одновременно. Это дает возможность писать чрезвычайно эффективные программы путем разделения их на отдельные исполняемые блоки и управления ходом выполнения всей программы в целом. Для многозадачности такого типа в MFC предусмотрены специальные средства поддержки.
С введением потоковой многозадачности возникла необходимость в специальном механизме, называемом синхронизацией. Синхронизация позволяет контролировать выполнение потоков (и процессов) строго определенным образом. В Win32 для синхронизации выделена целая подсистема. Библиотека классов MFC полностью поддерживает средства многозадачности.
Использование потоков
Потоковая многозадачность дает возможность программисту контролировать выполнение отдельных частей программы. Важно понимать, что все процессы имеют по крайней мере один поток выполнения. Он называется главным, первичным потоком. Но в пределах одного и того же процесса можно создавать несколько потоков. В общем случае, когда новый поток создается, он сразу же начинается выполняться. Таким образом, каждый процесс начинается с одного потока, к которому впоследствии могут добавляться дополнительные потоки. Когда они создаются, родительский процесс начинает выполняться не последовательно, а параллельно.
Потоки MFC
В MFC определены два типа потоков: интерфейсные и рабочие. Интерфейсный поток способен принимать и обрабатывать сообщения. Говоря языком MFC, интерфейсные потоки содержат канал сообщений. Главный поток MFC-программы (начинающийся при объявлении объекта класса CWinApp) является интерфейсным потоком. Рабочие потоки не принимают и не обрабатывают сообщения. Они обеспечивают дополнительные пути выполнения задачи внутри интерфейсного потока.
В MFC потоковая многозадачность реализуется с помощью класса CWinThread. Кстати, что производным от него является класс CWinApp, формирующий поток приложения.
При использовании классов, отвечающих за работу в многозадачном режиме, в программу следует включать стандартный библиотечный файл afxmt.h.
При создании многопотоковых программ наиболее часто используются именно рабочие потоки - необходимость в нескольких каналах сообщений возникает достаточно редко, однако во многих приложениях используются вспомогательные потоки, позволяющие вести фоновую обработку данных. Сосредоточимся поэтому на рабочих потоках (важно понимать, что на уровне API и рабочие, и интерфейсные потоки обрабатываются одинаково; различие между ними существует только в иерархии классов MFC.
Создание рабочего потока
Для создания рабочего потока предназначена функция AfxBeginThreadбиблиотеки MFC:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0, DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
Каждый поток внутри родительского процесса начинает свое выполнение с вызова специальной функции, называемой потоковой функцией. Выполнение потока продолжается до тех пор, пока не завершится его потоковая функция. Адрес данной функции (т.е. входная точка в поток) передается в параметре pfnThreadProc. Все потоковые функции должны иметь следующий прототип:
UINT pfnThreadProc(LPVOID pParam);
Значение параметра pParam функции AfxBeginThread передается потоковой функции в качестве параметра. Это 32-разрядное число может использоваться для любых целей.
Начальный приоритет потока указывается в параметре nPriority. Если этот параметр равен 0, то используются установки приоритета текущего (родительского) потока.
Каждый поток имеет свой собственный стек. Размер стека указывается в параметре nStackSize. Если этот параметр равен нулю (общепринятый подход), то создаваемому потоку будет выделен стек такого же размера, что и у родительского потока, а при необходимости, размер стека может быть увеличен.
Параметр dwCreateFlags определяет состояние выполнения потока. Если данный параметр равен нулю, поток начинает выполняться немедленно. Если значение этого параметра равно CREATE_SUSPEND, то поток создается временно приостановленным, т.е. ожидающим запуска. Чтобы запустить такой поток, нужно вызвать функцию CWinThread::ResumeThread.
Параметр lpSecurityAttrs является указателем на набор атрибутов прав доступа, относящийся к данному потоку. Если этот параметр равен NULL, то набор атрибутов будет унаследован от родительского окна.
При успешном завершении функция AfxBeginThread возвращает указатель на объект потока, в противном случае возвращает ноль. Данный указатель необходимо сохранять, если впоследствии предполагается обращение из родительского потока к созданному потоку (например, для изменения приоритета или для временного приостановления потока).
Рассмотрим пример создания рабочего потока для однодокументного приложения example при обработке сообщения о выборе пользователем пункта меню «Start». В качестве родительского потока выступает главный поток приложения. Рабочий поток после запуска осуществляет 100-кратный вывод некоторого изображения в окно приложения:
UINT MyThread(LPVOID pParam); // объявление функции потока
// реализация метода класса CExampleView (класса обликов),
// наследованного от базового CView из MFC.
void CExampleView::OnStart() // обработка сообщения от меню
{
//Создать новый поток. Функция потока имеет имя MyThread,
// в качестве параметра функции потока передается указатель
// на текущее окно просмотра для вывода в него изображения
AfxBeginThread(MyThread,this);
}
// определение функции потока
UINT MyThread(LPVOID pParam)
{
// через параметр передается указатель на окно просмотра
CExampleView *ptrView=(CExampleView *)pParam;
for(int i=0; i<100; i++)
{
CDC *dc=ptrView->GetDC(); // получить контекст отображения
CRect r;
ptrView->GetClientRect(&r); // получить клиентскую область окна
dc->TextOut(rand()%r.Width(),rand()%r.Height(),"*",1); // вывод
}
return 0;
}
Как уже упоминалось выше, поток выполняется до завершения своей потоковой функции. Поток может также «завершить сам себя¬ с помощью функции AfxEndThread библиотеки MFC. Параметр этого метода содержит статус завершения потока. Как правило, лучше давать потоку возможность нормально завершиться одновременно с потоковой функцией.
Иногда бывает необходимо приостановить поток на заданное количество миллисекунд. Это можно сделать, вызвав API-функцию Sleep.
Использование нескольких потоков
В программе может быть столько потоков, сколько необходимо. В следующем примере создается сразу два потока выполнения. Первый поток выполняет действия, аналогичные потоку предыдущего примера, второй поток каждые 2 секунды 50 раз выдает звуковой сигнал:
UINT MyThread1(LPVOID pParam);
UINT MyThread2(LPVOID pParam);
void CExampleView::OnStart()
{
AfxBeginThread(MyThread1,this);
AfxBeginThread(MyThread2,NULL); // параметр не передается
}
UINT MyThread1(LPVOID pParam)
{
CExampleView *ptrView=(CExampleView *)pParam;
for(int i=0; i<100; i++)
{ CDC *dc=ptrView->GetDC();
CRect r; ptrView->GetClientRect(&r);
dc->TextOut(rand()%r.Width(),rand()%r.Height(),"*",1);
}
return 0;
}
UINT MyThread2(LPVOID pParam)
{
for(int i=0; i<50; i++)
{ Sleep(2000);
MessageBeep(0);
}
return 0;
}
При работе с несколькими потоками для каждого из них должна быть определена своя потоковая функция и каждый из них должен начинаться отдельно. Все потоки процесса затем функционируют одновременно.
Остановка и возобновление выполнения потоков
Остановить выполнение потока можно с помощью метода SuspendThread класса CWinThread. В остановленном состоянии поток не выполняется. Продолжить выполнение потока можно с помощью метода ResumeThread класса CWinThread.
Каждый поток имеет связанный с ним счетчик остановок. Если этот счетчик равен нулю, значит поток выполняется нормально. При ненулевом значении счетчика поток находится в остановленном состоянии. С каждым вызовом метода SuspendThread значение счетчика остановок увеличивается на единицу. И, наоборот, с каждым вызовом функции ResumeThread значение счетчика остановок уменьшается на единицу. Остановленный поток может продолжить выполнение только после того, как значение счетчика достигнет нуля.
Управление приоритетами потоков
С каждым потоком связана определенная установка приоритета. Эта установка представляет собой комбинацию двух значений: значения общего класса приоритета процесса и значения приоритета самого потока относительно данного класса. Фактический приоритет потока определяется путем сложения класса приоритета процесса и уровня приоритета самого потока.
Приоритет потока показывает, сколько времени работы процессора требуется потоку. Для потоков с низким приоритетом требуется мало времени, а для потоков с приоритетом - много времени. Нужно заметить, что конечно же, количество времени, которое занимает поток у процессора, существенным образом влияет на характеристики выполнения потока и его взаимодействие с другими, выполняющимися в данный момент потоками.
Получить класс приоритета процесса можно с помощью функции GetPriorityClass, а установить класс приоритета можно с помощью функции SetPriorityClass. Обе эти функции являются API-функциями и не входят в класс CWinThread.
Ниже показаны константы, соответствующие классам приоритетов в порядке убывания:
-
REALTIME_PRIORITY_CLASS
-
HIGH_PRIORITY_CLASS
-
NORMAL_PRIORITY_CLASS
-
IDLE_PRIORITY_CLASS
По умолчанию программе присваивается приоритет NORMAL_PRIORITY_CLASS. Как правило, причин менять его нет. Фактически, изменение приоритета процесса может негативно сказаться на производительности всей системы. Так например, увеличение класса приоритета программы до REALTIME_PRIORITY_CLASS приведет к захвату программой всех ресурсов процессора.
Приоритет процесса (независимо от класса приоритета) говорит о том, сколько времени процессора занимает отдельный поток в пределах своего процесса. При создании потока ему присваивается нормальный приоритет. Но это значение можно изменить, причем даже во время выполнения потока.
Приоритеты потоков контролируются методами класса CWinThread. Определить значение приоритета можно с помощью метода GetThreadPriority, а изменить его - с помощью метода SetThreadPriority
Ниже приведены константы, соответствующие установкам приоритетов в порядке убывания:
-
THREAD_PRIORITY_TIME_CRITICAL
-
THREAD_PRIORITY_HIGHEST
-
THREAD_PRIORITY_ABOVE_NORMAL
-
THREAD_PRIORITY_NORMAL
-
THREAD_PRIORITY_BELOW_NORMAL
-
THREAD_PRIORITY_LOWEST
-
THREAD_PRIORITY_IDLE
Благодаря различным сочетаниям значений приоритета процесса и приоритета потока в Win32 поддерживается 31 различная установка приоритета.
Синхронизация потоков
Иногда при работе с несколькими потоками или процессами появляется необходимость синхронизировать выполнение двух или более из них. Причина этого чаще всего заключается в том, что два или более потоков могут требовать доступ к разделяемому ресурсу, который реально не может быть предоставлен сразу нескольким потокам. Разделяемым называется ресурс, доступ к которому могут одновременно получать несколько выполняющихся задач.
Механизм, обеспечивающий процесс синхронизации, называется ограничением доступа. Необходимость в нем возникает также в тех случаях, когда один поток ожидает события, генерируемого другим потоком. Естественно, должен существовать какой-то способ, с помощью которого первой поток будет приостановлен до совершения события. После этого поток должен продолжить свое выполнение.
Характеристики
Тип файла документ
Документы такого типа открываются такими программами, как Microsoft Office Word на компьютерах Windows, Apple Pages на компьютерах Mac, Open Office - бесплатная альтернатива на различных платформах, в том числе Linux. Наиболее простым и современным решением будут Google документы, так как открываются онлайн без скачивания прямо в браузере на любой платформе. Существуют российские качественные аналоги, например от Яндекса.
Будьте внимательны на мобильных устройствах, так как там используются упрощённый функционал даже в официальном приложении от Microsoft, поэтому для просмотра скачивайте PDF-версию. А если нужно редактировать файл, то используйте оригинальный файл.
Файлы такого типа обычно разбиты на страницы, а текст может быть форматированным (жирный, курсив, выбор шрифта, таблицы и т.п.), а также в него можно добавлять изображения. Формат идеально подходит для рефератов, докладов и РПЗ курсовых проектов, которые необходимо распечатать. Кстати перед печатью также сохраняйте файл в PDF, так как принтер может начудить со шрифтами.