Создание и работа с потоками — КиберПедия 

Типы оградительных сооружений в морском порту: По расположению оградительных сооружений в плане различают волноломы, обе оконечности...

Архитектура электронного правительства: Единая архитектура – это методологический подход при создании системы управления государства, который строится...

Создание и работа с потоками

2020-11-19 474
Создание и работа с потоками 0.00 из 5.00 0 оценок
Заказать работу

Каждый поток начинает свое выполнение с некоторой входной функции. У функции должен быть следующий прототип:

DWORD WINAPI ThreadProc (PVOID pPararn);

Функция потока может выполнять абсолютно любые задачи. Ниже приведена пустая функция, которая ничего не делает.

DWORD WINAPI ThreadProc(PVOID pPararn);

{

return 0;

}

Когда эта функция закончит выполнение – поток автоматически завершится. В этот момент система выполняет следующие действия:

  • Останавливает поток
  • Освобождает стек
  • Счетчик пользователей для объекта ядра потока уменьшится на 1.

Когда счетчик объекта ядра обнуляется – система его удаляет. Получается, что объект ядра может жить дольше, чем сам поток. Это сделано для того, чтобы остальные части программы могли получать доступ к информации о потоке, даже если его уже не существует. Например, если надо узнать код завершения потока.

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

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

Итак, у нас есть потоковая функция. Давайте заставим систему создать для нас поток, который выполнит эту функцию.

Создание потока

Создание потока в Windows происходит с помощью вызова API фукнции:

HANDLE CreateThread(PSECURITY_ATTRIBUTES psa, DWORD cbStack,

PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam, DWORD tdwCreate, PDWORD pdwThreadID);

Вызов этой функции создает объект ядра “поток” и возвращает его дескриптор. Система выделяет память под стек нового потока из адресного пространства процесса, инициализирует структуры данных потока и передает управление потоковой функции. Новый поток выполняется в контексте того же процесса, что и родительский поток. Поэтому он имеет доступ ко всем дескрипторам процесса, адресному пространству. Поэтому все потоки могут легко взаимодействовать друг с другом.

Параметры функции CreateThread следующие:

  • psa – указатель на структуру SECURITY_ATTRIBUTES. Если вы хотите, чтобы потоку были присвоены параметры защиты по умолчанию – передайте сюда NULL.
  • cbStack – размер стека потока. Если параметр равен нулю – используется размер по умолчанию. Если вы передаете не нулевое значение, система выберет большее между настройками текущего исполняемого файла и вашим значением. Этот параметр резервирует только адресное пространство, а физическая память выделяется по мере необходимости.
  • pfnStartAddr – это указатель на потоковую функцию. Прототип функции мы рассмотрели выше.
  • pParam – произвольное значение. Этот параметр идентичен параметру потоковой функции. CreateThread передаст этот параметр в потоковую функцию. Это может быть число, либо указатель на структуру данных. Можно создавать несколько потоков с одной и той же потоковой функцией. Каждому потоку можно передавать свое значение.

Внимание, не передавайте сюда указатель на локальные переменные! Т.к. родительский поток работает одновременно с новым – локальные переменные могут выйти из области видимости и разрушиться компилятором. В то время, как новый поток будет пытаться получить к ним доступ.

  • tdwCreate – дополнительные параметры создания потока. Может принимать значение 0 если надо начать выполнение потока немедленно, либо CREATE_SlJSPENDED. В последнем случае система выполняет всю инициализацию, но не запускает выполнение потока. Поток можно запустить в любой момент, вызвав WinAPI функцию ResumeThread.
  • pdwThreadID – указатель на переменную, которая на выходе будет содержать идентификатор потока. Windows 2k+ позволяет передавать сюда NULL, если Вам не нужно это значение. Однако, рекомендуется всегда передавать адрес переменной для совместимости с более ранними ОС.

Завершение потока

Поток может завершиться в следующих случаях:

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

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

  • любые С++-объекты, созданные данным потоком, уничтожаются соответствующими деструкторами;
  • система корректно освобождает память, которую занимал стек потока;
  • система устанавливает код завершения данного потока (поддерживаемый объектом ядра "поток») — его и возвращает Ваша функция потока;
  • счетчик пользователей данного объекта ядра "поток" уменьшается на 1

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

Завершение потока принудительным образом извне (TerminateThread, завершение процесса) может вызвать проблемы не только с корректным освобождением ресурсов, но и с логикой работы программы. Например, “убиенный” поток не освободит доступ к занятым ресурсам и объектам синхронизации. В результате остальная часть программы может повести себя непредсказуемым образом.

Практическая часть

Пример 1

В первом примере разработаем диалоговое многопоточное приложение.

Сначала напишем функцию, которая будет сигнализировать системным динамиком. Её будем вызывать при создании потока.

DWORD WINAPI OurFunction (PVOID pParam)

{

Beep (200, 1000); //первый параметр–частота, второй – длительность

return (0);

}

Вызов создания потока можно интегрировать в функцию нажатия кнопки или на любое другое событие по вашему выбору. Для вызова потока по кнопке достаточно в код вставить следующий фрагмент:

DWORD dwID;

CreateThread(NULL, 0, OurFunction, NULL, NULL, &dwID);

После запуска собранного приложения и нажатия на требуемую кнопку мы можем услышать характерное «пищание» системного динамика (в случае если динамик подключён и активирован).

Все способы, за исключением рекомендуемого, являются нежелательными и должны использоваться только в форс-мажорных обстоятельствах.

Функция потока, возвращая управление, гарантирует корректную очистку всех ресурсов, принадлежащих данному потоку. При этом:

- любые С++ объекты, созданные данным потоком, уничтожаются соответствующими деструкторами;

- система корректно освобождает память, которую занимал стек потока;

- система устанавливает код завершения данного потока. Его функция и возвращает;

- счетчик пользователей данного объекта ядра (поток) уменьшается на 1.

При желании немедленно завершить поток изнутри используют функцию ExitThread(DWORD dwExitCode).

При этом освобождаются все ресурсы ОС, выделенные данному потоку, но С С++ ресурсы (например, объекты классов С++) не очищаются. Именно поэтому не рекомендовано завершать поток, используя эту функцию.

Если появилась необходимость уничтожить поток снаружи, то это может сделать функция TeminateThread.

Пример 2

Дана последовательность натуральных чисел a 0, …, a 99. Создать многопоточное приложение для поиска суммы квадратов Σai Вычисления должны независимо выполнять четыре потока.

Обсуждение. Разобьем последовательность чисел на четыре части и создадим четыре потока, каждый из которых будет вычислять суммы квадратов элементов в отдельной части последовательности. Главный поток создаст дочерние потоки, соберет данные и вычислит окончательный результат, после того, как отработают четыре дочерних потока (рис. 1.1). Приложение сделаем консольным.

Рис. 1.1 Схема потоков для примера 2

#include <stdio.h>

#include <conio.h>

#include <windows.h>

const int n = 4;

int a[100];

DWORD WINAPI ThreadFunc(PVOID pvParam)

{

int num,sum = 0,i;

num = 25*(*((int *)pvParam));

for(i=num;i<num+25;i++) sum += a[i]*a[i];

*(int*)pvParam = sum;

DWORD dwResult = num;

return dwResult;

}

int main(int argc, char** argv)

{

int x[n];

int i,rez = 0;

DWORD dwThreadId[n],dw,dwResult[n];

HANDLE hThread[n];

for (i=0;i<100;i++) a[i] = i;

//создание n дочерних потоков

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

{

x[i] = i;

hThread[i] = CreateThread(NULL,0,ThreadFunc,(PVOID)&x[i], 0, &dwThreadId[i]);

if(!hThread) printf("main process: thread %d not execute!",i);

}

// ожидание завершения n потоков

dw = WaitForMultipleObjects(n,hThread,TRUE,INFINITE);

// получение значений, переданных потоком в return

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

{

GetExitCodeThread(hThread[i],&dwResult[i]);

printf("%d\n",(int)dwResult[i]);

}

for(i=0;i<n;i++) rez+=x[i];

printf ("\ n Сумма квадратов = % d ", rez);

getch ();

return 0;

}

Варианты заданий

1. Даны последовательности символов А = {а0…аn–1} и С = {с0…ск–1}. В общем случае n ≠ k. Создать многопоточное приложение, определяющее, совпадают ли посимвольно строки А и С. Количество потоков является входным параметром программы, количество символов в строках может быть не кратно количеству потоков.

2. Дана последовательность символов С = {с0…сn–1} и символ b. Создать многопоточное приложение для определения количество вхождений символа b в строку C. Количество потоков является входным параметром программы, количество символов в строке может быть не кратно количеству потоков.

3. Дана последовательность натуральных чисел {a0…an–1}. Создать многопоточное приложение для поиска суммы Σai. Количество потоков является входным параметром программы, потоки проводят вычисления независимо друг от друга, количество символов в строке может быть не кратно количеству потоков.

4. Дана последовательность натуральных чисел {a0…an–1}. Создать многопоточное приложение для поиска произведения чисел a0*а1*…*an–1. Количество потоков является входным параметром программы, потоки проводят вычисления независимо друг от друга, количество символов в строке может быть не кратно количеству потоков.

5. Дана последовательность натуральных чисел {a0…an–1}. Создать многопоточное приложение для поиска максимального ai. Количество потоков является входным параметром программы, потоки проводят вычисления независимо друг от друга, количество символов в строке может быть не кратно количеству потоков.

6. Дана последовательность натуральных чисел {a0…an–1}. Создать многопоточное приложение для поиска минимального ai. Количество потоков является входным параметром программы, потоки проводят вычисления независимо друг от друга, количество символов в строке может быть не кратно количеству потоков.

7. Дана последовательность натуральных чисел {a0…an–1}. Создать многопоточное приложение для поиска всех ai, являющихся простыми числами. Количество потоков является входным параметром программы, потоки проводят вычисления независимо друг от друга, количество символов в строке может быть не кратно количеству потоков.

8. Дана последовательность натуральных чисел {a0…an–1}. Создать многопоточное приложение для поиска всех ai, являющихся квадратами, любого натурального числа.

9. Дана последовательность натуральных чисел {a0…an–1}. Создать многопоточное приложение для вычисления выражения a0-а1+a2-а3+a4-а5+...

10. Дана последовательность натуральных чисел {a0…an–1}. Создать многопоточное приложение для поиска суммы Σai, где ai – четные числа.

11. Изготовление знаменитого самурайского меча – катаны происходит в три этапа. Сначала младший ученик мастера выковывает заготовку будущего меча. Затем старший ученик мастера закаливает меч в трех водах – кипящей, студеной и теплой. И в конце мастер собственноручно изготавливает рукоять меча и наносит узоры. Требуется создать многопоточное приложение, в котором мастер и его ученики представлены разными потоками. Изготовление меча представить в виде разных арифметических операций над глобальной переменной.

12. Командиру N-ской ВЧ полковнику Кузнецову требуется перемножить два секретных числа. Полковник Кузнецов вызывает дежурного по части лейтенанта Смирнова и требует в течение получаса предоставить ему ответ. Лейтенант Смирнов будит старшего по караулу сержанта Петрова и приказывает ему в 15 минут предоставить ответ. Сержант Петров вызывает к себе рядового Иванова, бывшего студента, и поручает ему ответственное задание по определению произведения. Рядовой Иванов успешно справляется с поставленной задачей и ответ по цепочке передается полковнику Кузнецову. Требуется создать многопоточное приложение, в котором все военнослужащие от полковника до рядового моделируются потоками одного вида.

13. Даны результаты сдачи экзамена по курсу «Параллельные и распределённые вычисления» по группам. Требуется создать многопоточное приложение, вычисляющее средний балл. Потоки должны осуществлять вычисления параллельно по группам. Количество потоков является входным параметром программы, потоки проводят вычисления независимо друг от друга, количество групп может быть не кратно количеству потоков.

14. Охранное агентство разработало новую систему управления электронными замками. Для открытия двери клиент обязан произнести произвольную фразу из 25 слов. В этой фразе должно встречаться заранее оговоренное слово, причем только один раз. Требуется создать многопоточное приложение, управляющее замком. Потоки должны осуществлять сравнение параллельно по словам.

15. Среди студентов нашего университета проведен опрос с целью определения процента студентов, знающих точную формулировку правила Буравчика. В результате собраны данные о количестве знатоков на каждом направлении по группам. Известно, что всего в филиале обучается 500 студентов. Требуется создать многопоточное приложение для определения процента знающих правило Буравчика студентов. Потоки должны осуществлять поиск количества знатоков по факультету. Искомый процент определяет главный поток. Количество потоков является входным параметром программы, потоки проводят вычисления независимо друг от друга, количество направлений может быть не кратно количеству потоков.

16. Руководство заготовительной компании «Рога и Копыта» проводит соревнование по заготовке рогов среди своих региональных отделений. Все данные по результатам заготовки рогов (заготовитель, его результат) хранятся в общей базе данных по отделениям. Требуется создать многопоточное приложение для поиска лучшего заготовителя. Потоки должны осуществлять поиск победителя параллельно по отделениям. Главный поток определит победителя. Количество потоков является входным параметром программы, потоки проводят вычисления независимо друг от друга, количество отделений может быть не кратно количеству потоков.

 


Поделиться с друзьями:

Типы оградительных сооружений в морском порту: По расположению оградительных сооружений в плане различают волноломы, обе оконечности...

Наброски и зарисовки растений, плодов, цветов: Освоить конструктивное построение структуры дерева через зарисовки отдельных деревьев, группы деревьев...

Индивидуальные очистные сооружения: К классу индивидуальных очистных сооружений относят сооружения, пропускная способность которых...

Типы сооружений для обработки осадков: Септиками называются сооружения, в которых одновременно происходят осветление сточной жидкости...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.052 с.