пользователей: 30398
предметов: 12406
вопросов: 234839
Конспект-online
РЕГИСТРАЦИЯ ЭКСКУРСИЯ

Поток. Определение, особенности. Создание и завершение потока. Пример. Функции WaitFor…

Поток определяет последовательность исполнения кода в процессе.

Поток состоит из двух компонентов:

  • объекта ядра, через который операционная система управляет потоком. Там же хранится статистическая информация о потоке;
  • стека потока, который содержит параметры всех функций и локальные переменные, необходимые потоку для выполнения кода.

При инициализации процесса система всегда создает первичный поток.

Большинство приложений обходятся одним потоком, однако, процессы могут создавать множество потоков. Это позволяет добиться  минимального простоя процессора и тем самым увеличить эффективность работы.

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

Потоки могут исполнять один и тот же код и манипулировать одними и теми же данными, а также совместно использовать описатели объектов ядра, поскольку таблица описателей создается не в отдельных потоках, а в процессах.

Процессы используют куда больше системных ресурсов, чем потоки. Причина кроется в адресном пространстве. Создание виртуального адресного пространства для процесса требует значительных системных ресурсов. При этом ведется масса всяческой статистики, на что уходит немало памяти. В адресное пространство загружаются EXE- и DLL-файлы, а значит, нужны файловые ресурсы.

Если Вы хотите создать дополнительные потоки, нужно вызывать из первичного потока функцию CreateThread:

HANDLE CreateThread(

PSECURITY_ATTRIBUTES psa,

DWORD cbStack,

PTHREAD_START_ROUTINE pfnStartAddr,

PVOID pvParam,

DWORD tdwCreate,

PDWORD pdwThreadID);

При каждом вызове этой функции система создает объект ядра «поток».

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

Параметр psa является указателем на структуру SECURITY_ATTRIBUTES Если Вы хотите, чтобы объекту ядра "поток" были присвоены атрибуты защиты по умолчанию (что чаще всего и бывает), передайте в этом параметре NULL.

cbStack определяет часть адресного пространства, отводимого под стек (в байтах). Если вы обнулили этот параметр, то размер стека будет найден в коде приложения (компоновщик позволяет управлять размером стека для приложения). По умолчанию 1 Мб.

pfnStartAddr – определяет адрес стартовой функции потока. Функция должна иметь вид  INT … (PVOID pvParam); Параметр pvParam идентичен параметру pvParam, передаваемому в CreateThread. По сути функция создания потока передает этот параметр по эстафете стартовой функции потока. В данной функции должен содержаться весь код потока, так как по ее завершению поток будет завершен.

tdwCreate – определяет дополнительные флаги, управляющие созданием потока.0 – поток начинает выполняться немедленно, CREATE_SUSPENDED – поток приостанавливает свое выполнение до дальнейших указаний.

CreateThread – это адрес переменной типа DWORD, в которой функция возвращает идентификатор, приписанный системой новому потоку.

 

Поток можно завершить четырьмя способами:

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

1.  Возврат управления функцией потока

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

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

2.  Функция ExitThread

Поток можно завершить принудительно, вызвав:

  VOID ExitThread(DWORD dwExitCоde);

При этом освобождаются все ресурсы операционной системы, выделенные данному потоку, но C/C++ - pеcypcы (например, объекты, созданные из С++-классов) не очищаются.

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

3.  Функция TerminateThread

Вызов этой функции также завершает поток:

        BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);

В отличие от ExitThread, которая уничтожает только вызывающий поток, эта функция завершает поток, указанный в параметре hThread. В параметр dwExitCode Вы помещаете значение, которое система рассматривает как код завершения потока. После того как поток будет уничтожен, счетчик пользователей его объекта ядра «поток» уменьшится на 1 .

TerminateThread — функция асинхронная, т, e. она сообщает системе, что Вы хотите завершить поток, но к тому времени, когда она вернет управление, поток может быть еще не уничтожен. Если нужно точно знать момент завершения потока, то нужно использовать WaitForSingleObject или аналогичную функцию, передав ей описатель этого потока.

Если завершается процесс

Функции ExitProcess и TerminateProcess, тоже завершают потоки.

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

Однако эти две функции уничтожают потоки принудительно — так, будто для каждого из них вызывается функция TerminateThread.

Это означает, что очистка проводится некорректно, деструкторы С++-объектов не вызываются, данные на диск не сбрасываются и т.д.

Что происходит при завершении потока:

Освобождаются все описатели User-объектов, принадлежавших потоку. В Windows большинство объектов принадлежит процессу, содержащему поток, из которого они были созданы. Сам поток владеет только двумя User-объектами, окнами и ловушками (hooks). Когда поток, создавший такие объекты, завершается, система уничтожает их автоматически. Прочие объекты разрушаются, только когда завершается владевший ими процесс.

Код завершения потока меняется со STILL_ACTIVE на код, переданный в функцию ExitThread или TerminateThread.

Объект ядра "поток" переводится в свободное состояние.

Если данный поток является последним активным потоком в процессе, завершается и сам процесс.

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

Примеры создания и завершения процесса:

int _tmain(int argc, _TCHAR* argv[])
{
 cout<<"Number: "<<endl;
 cin>>n;  sum=0.0; odf=1.0;
   HANDLE *hThreads=new HANDLE[1];
   hThreads[0] = CreateThread(NULL, 0, &odinnafac, NULL, 0, NULL);
   hThreads[1] = CreateThread(NULL, 0, &ThreadProc, NULL, 0, NULL);
 
WaitForSingleObject(hThreads[0], INFINITE); WaitForSingleObject(hThreads[1], INFINITE);
   CloseHandle(hThreads[0]);
   CloseHandle(hThreads[1]);
}
DWORD WINAPI odinnafac(CONST LPVOID lpParam)
  {
  for (int k = 1; k <= n; k++)
    {
     odf=odf*k;
   }
    odf=1.0/odf;
  ExitThread(0);
  }
DWORD WINAPI ThreadProc(CONST LPVOID lpParam)
{  nfact=1;
   for ( i = 1; i <= n; i++)
  …
  }
  ExitThread(0);
 }
 
Функции для синхронизации потоков:
Каждый объект ядра может находиться в двух состояниях: свободном или занятом. Идея синхронизации заключается в том, что поток "усыпляет" себя до того как объект ядра не освободится.

DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);

С помощью данной функции поток сообщает системе, что будет ожидать освобождения объекта hObject в течении dwMilliseconds миллисекунд. Если через заданное время объект не освободится, ОС продолжит выполнение потока.
Функция возвращает следующие значения:
WAIT_OBJECT_0 – Объект перешел в свободное состояние
WAIT_TIMEOUT – Истекло время ожидания
WAIT_FAILED – Ошибка
Если dwTimeOut равно 0, то функция просто вернет состояние объекта. Если dwTimeOut равно значению INFINITE, то время ожидания равно бесконечности. 

DWORD WaitForMultipleObjects( DWORD dwCount, CONST HANDLE* phObjects, BOOL fWaitAll, DWORD dwMilliseconds);

Функция ожидает освобождения всех или одного объекта из списка phObjects (указатель на массив переменных типа HANDLE). 
dwCount – количество объектов в массиве. Максимальное кол-во – 64.
fWaitAll – определяет режим ожидания. Если значение параметра равно True, то функция будет ждать освобождения всех объектов, иначе любого из передаваемого списка.

BOOL WINAPI GetThreadTimes(HANDLE hThread, LPFILETIME lpCreationTime, LPFILETIME lpExitTime, LPFILETIME lpKernelTime,

 LPFILETIME lpUserTime);

возвращает время, затраченное на выполнение потока. Параметры: lpCreationTime – время создания потока, lpExitTime - время завершения потока, lpKernelTime  - время затраченное на выполнение кода ядра, lpUserTime - время затраченное на выполнение кода потока.
 

27.12.2015; 15:16
хиты: 0
рейтинг:0
Точные науки
информатика
для добавления комментариев необходимо авторизироваться.
  Copyright © 2013-2024. All Rights Reserved. помощь