![]() |
|
Модераторы: Daevaorn |
![]()
|
|
| LZero |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 27.2.2003 Репутация: нет Всего: нет |
Вопрос:
Есть ли в Visual C++ Thread-ы, аналогичные тем, что в C++ Builder, или если таковых нету, то как сделать подобное на VCPP??? |
|||
|
||||
| Baa |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 2639 Регистрация: 12.4.2002 Где: Москва Репутация: 8 Всего: 12 |
См. функ. CreateThread, AfxBeginThread
-------------------- "Duty is everything; the greatest of joys, the deepest of sorrows" Aribeth de Tylmarande |
|||
|
||||
| Georg4 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 961 Регистрация: 2.11.2002 Репутация: нет Всего: 10 |
Существует несколько способов запустить поток в приложениях, составленных на языке программирования С.
1. Можно использовать функцию CreateThread, которая входит в программный интерфейс ОС Microsoft Windows NT. Этот способ предоставляет пользователю наибольшие возможности по управлению запущенными задачами, позволяя, в частности, присваивать запущенным задачам атрибуты защиты и создавать задачи в приостановленном состоянии. 2. В вашем распоряжении имеется функция из библиотеки системы разработки Microsoft Visual C++ c названием _beginthread. Потоки, созданные с использованием этой функции, могут обращаться ко всем стандартным функциям библиотеки и к переменной errno (системные ошибки). 3. Можно запустить поток при помощи _beginthreadex, которая определена в библиотеке Microsoft Visual C++. Имеет возможности, аналогичные функции CreateThread. 4. При работе с библиотекой MFC запуск нового потока осуществляется через функцию AfxBeginThread. Рассмотрим все эти способы. Функция CreateThread Поток может быть создан и из конструктора класса, от которого он унаследован. В этом случае необходимо вызвать функцию CreateThread. Прототип функции CreateThread, с помощью которой процессы могут создавать задачи, представляется в следующем виде: HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpThreadAttributes, //атрибуты защиты DWORD dwStackSize, //начальный размер стека в байтах LPTHREAD_START_ROUTINE lpStartAddress, //адрес функции задачи LPVOID lpParameter, //параметры для задачи DWORD dwCreationFlags, //параметры создания задачи LPWORD lpThreadId //адрес переменной для идентификатора задачи ); Через параметр lpThreadAttributes передается адрес структуры SECURITY_ATTRIBUTES, определяющей атрибуты защиты для создаваемой задачи, или значение NULL. В последнем случае для задачи будут использованы атрибуты защиты, принятые по умолчанию. Это означает, что идентификатор созданной задачи можно использовать в любых функциях, выполняющих любые операции над задачами. Указывая атрибуты защиты, можно запретить использование тех или иных функций. Приведем структуру SECURITY_ATTRIBUTES: typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; //размер структуры в байтах LPVOID lpSecurityDescriptor; //указатель на дескриптор защиты BOOL bInheritHandle; //флаг наследования идентификатора } SECURITY_ATTRIBUTES; При подготовке структуры в поле nLength следует записать размер структуры SECURITY_ATTRIBUTES. Поле указателя на дескриптор защиты lpSecurityDescriptor не заполняется приложением непосредственно. Вместо этого для установки дескриптора защиты используется набор функций, которым в качестве одного из параметров передается указатель на структуру SECURITY_ATTRIBUTES. Эти функции подробно описаны в SDK (Software Development Kit – набор инструментальных средств разработки ПО, включающий библиотеки, заголовочный файлы, help-файлы, документацию и т.д.). Параметр dwStackSize функции CreateThread позволяет указать начальный размер стека для запускаемой задачи. Если указать для этого параметра нулевое значение, размер стека запущенной задачи будет равен размеру стека главной задачи процесса. При необходимости размер стека автоматически увеличивается. Таким образом, первые два параметра функции CreateThread в большинстве случаев могут быть указаны соответственно как NULL и 0. Параметр lpStartAddress задает адрес функции, которая будет выполняться как отдельная задача. Здесь можно указать просто имя этой функции. Функция задачи имеет один 32-разрядный параметр и возвращает 32-разрядное значение. Указанный параметр передается функции CreateThread через параметр lpParameter. Если значение параметра dwCreationFlags равно нулю, после вызова функции CreateThread задача немедленно начнет свое выполнение. Если же в этом параметре указать значение CREATE_SUSPENDED, задача будет загружена, но приостановлена. Возобновить выполнение приостановленной задачи можно будет позже с помощью функции ResumeThread. И, наконец, через параметр lpThreadId необходимо передать адрес переменной типа DWORD, в которую будет записан системный номер созданной задачи. В случае успеха функция CreateThread возвращает идентификатор задачи, пользуясь которым можно выполнять над задачей те или иные операции. Не путайте этот идентификатор с системным номером задачи. При ошибке функция CreateThread возвращает значение NULL. Пример Данный пример запускает поток с функцией задачи ThreadRoutine. Атрибуты защиты не используются, размер стека запускаемой задачи равен размеру стека главной задачи процесса. Функция имеет один параметр, который в нашем случае будет принимать значение hwndChild. Для проверки значения, возвращаемого функцией задачи, процесс может воспользоваться функцией GetExitCodeThread. hThread = CreateThread(NULL, 0, ThreadRoutine, hwndChild, &dwIDThread); DWORD ThreadRoutine (HWND hwnd) { //… return 0; } Функция _beginthread. Создание мультизадачного приложения, в котором используются функции стандартной библиотеки С, может привести к возникновению проблем. Данные библиотеки не были рассчитаны возможную реализацию параллельной работы нескольких задач. Возможные проблемы связаны, например, с использованием стандартных глобальных переменных, таких, как errno, и функций, работающих с глобальными переменными. Так в переменную errno записывается код ошибки. В мультизадачных приложениях содержимое этой переменной м/б установлено из любой задачи, поэтому для каждой задачи необходимо предусмотреть свою собственную глобальную переменную errno. Для разрешения этой проблемы в системе разработки Microsoft Visual C++ предусмотрены отдельные библиотеки для создания однозадачных (libc.lib) и мультизадачных (libmc.lib) приложений. После подключения мультизадачной библиотеки (меню Build -> Setting -> Category в окне Project Setting -> Code Generation -> Multithreaded в списке Use run-time library) можно использовать функции _beginthread и _beginthreadex для запуска задач. Прототип функции выглядит следующим образом: unsigned long _beginthread( void (*StartAddress) (void*),//адрес функции задачи unsigned uStackSize,//начальный размер стека в байтах void *ArgList //параметры для задачи); Заметим, что функция задачи, запускаемая таким образом, не возвращает никакого значения. Ее адрес передается функции _beginthread через параметр StartAddress. Через параметр ArgList функции передается один параметр. Так же как и в случае с функцией CreateThread, при нулевом значении параметра uStackSize для задачи создается стек такого же размера, как и для главной задачи. В случае успеха функция _beginthread возвращает идентификатор запущенной задачи. Если же произошла ошибка, возвращается значение –1. Пример _beginthread(ThreadRountine, 0, (void*) (Param)); void ThreadRountine(void *Param) { //… _endthread(); } Заметим, что для завершения задачи здесь используется функция _endthread(), не имеющая параметров. С помощью этой функции можно завершить задачу в любом месте функции задачи. Если данную функцию не использовать, то после выполнения всей последовательности операций функции задачи произойдет неявный вызов функции _endthread(), и, как следствие, задача завершится. Функция _beginthreadex. В том случае, если : · нужны возможности функции Create Thread (например, необходимо создать задачу в приостановленном состоянии); · необходимо использовать функции библиотеки транслятора имеет смысл использовать функцию _beginthreadex Прототип функции выглядит следующим образом: unsigned long _beginthreadex( void *Security, //указатель на дескриптор защиты unsigned StackSize, //начальный размер стека void (*StartAddress) (void*), //адрес функции задачи void *ArgList, //параметры для задачи unsigned Initflag, //параметры создания задачи unsigned *ThrdAddr //адрес созданной задачи ); Для запуска задачи в приостановленном состоянии через параметр Initflag необходимо передать значение CREATE_SUSPENDED. Функция задачи, которая запускается с помощью функции _beginthreadex, имеет один параметр и возвращает 32-разрядное значение, аналогично функции задачи, запускаемой функцией Create Thread. Для завершения своего выполнения функция задачи должна использовать либо оператор возврата, либо функцию _endthreadex, не имеющую параметров. В случае успеха функция _beginthreadex возвращает идентификатор запущенной задачи. Если произошла ошибка, то 0 (не –1). MFC- функция AfxBeginThread. Библиотека MFC – это обширная библиотека классов C++ , позволяющая создавать программы Windows GUI (Grafical User Interfase – графический пользовательский интерфейс). Хотя программы с графическим интерфейсом можно создать и без классов библиотеки MFC, классы библиотеки MFC дают возможность упростить написание программ и предоставляют для этого множество компонентов высокого уровня. CWinApp - это базовый объект из которого получается объект(программа) для Windows. CWinApp отвечает за инициализацию и запуск приложения Windows. Все приложения MFC используют этот класс, который может быть включен только один раз. Ваш объект производный от CWinApp должен быть объявлен на глобальном уровне. CWinApp порожден от CWinThread: .... class CWinApp : public CWinThread { ..... Класс CWinThread - это потоки выполняемые в приложении. Главный поток - это обычно CWinApp. При работе с библиотекой MFC можно выделить два типа потоков: · UI-потоки, которые работают с сообщениями и обычно решают задачи, связанные с пользовательским интерфейсом, · рабочие потоки, которые решают другие задачи и не ведут обработки сообщений. Как раз CWinApp порожден с помощью интерфейсного потока. Создается поток функцией AfxBeginThread, которая имеет две версии в соотвествии с типами потоков. // Создание рабочего потока CwinThread* AfxBeginThread (AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateSize =0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL); //Создание интерфейсного потока CWinThread* AfxBeginThread ( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL ); В первом случае передается адрес процедуры, а во втором адрес класса. Примечание Функция AfxBeginThread инициализирует библиотеку MFC для использования в многопотоковой программе. Затем она вызывает функцию _beginthreadex библиотеки периода выполнения, инициализирующую библиотеку программы с несколькими потоками, а затем для запуска потока вызывает функцию WIN32 API ::CreateThread. Если функцию AfxBeginThread использовать для запуска потока программы MFC, то можно без проблем использовать классы MFC, функции библиотеки периода выполнения и WIN32 API В программах MFC нельзя запускать новый поток, непосредственно вызывая библиотечные функции _beginthread и _beginthreadex или функцию ::CreateThread интерфейса WIN32 API Функция AfxBeginThread запускает новый поток и сразу возвращает управление. С этого момента оба потока (новый и вызвавший функцию AfxBeginThread) работают одновременно. Поток будет оставаться активным до тех пор, пока выполняется запущенная в нем функция. Когда функция закончит свою работу, поток будет уничтожен. Функция потока определяется следующим образом: UINT ThreadFunction (LPVOID pParam); Примечание Функция потока возвращает значение типа UINT, называющееся кодом возврата. Код возврата может быть прочитан другими потоками. При нормальном завершении функции потока возвращается значение 0. Хотя при желании можно использовать лювое возвращаемое значение, потому что оно читается и интерпретируется только вашей собственной программой. (Нельзя возвращать специальное значение STILL_ACTIVE, равное 0x00000103L, показывающее, что поток продолжает выполняться.) Информация для нового потока задается указателем pParam, передаваемым в функцию потока, который может содержать адрес простого значения (типа int) или структуры, включающей произвольное количество информации. Новый поток передает информацию в начальный поток (в дополнение к коду возврата), присваивая значение переменным, на которое указывает параметр pParam. Для выполнения описанной процедуры работа потоков должна быть синхронизирована. Отметим, что при создании интерфейсного потока 32-разрядный параметр не передается. Четыре последних параметра функции AfxBeginThread() имеют стандартные значения. Параметр nPriority задает приоритет нового потока. Если параметр dwCreateFlags равен 0 (стандартное значение), то новый поток начинает выполняться немедленно. Если же присвоено значение CREATE_SUSPENDED, то он не запустится до вызова функции CWinthread::ResumeThread. Параметрам nStack и lpSecurityAttrs почти всегда присваиваются стандартные значения, определяющие размер стека для потока и атрибуты защиты. Пример Данный фрагмент программы запускает новый поток, выполняющий функцию ThreadFunction и вызываемые ею функции. UINT ThreadFunction (LPVOID pParam) //описание функции потока { //Операторы и вызовы функций, которые д/б выполнены //новым потоком return 0; //окончание потока и передача кода возврата 0 } void SomeFunction (void) { //… int Code =1; CWinThread *PWinThread; PwinThread = AfxBeginThread (ThreadFunction, &Code); //… } Ограничения на использование MFC-классов Потоки, выполняемые в одном процессе, совместно используют определенную область памяти и глобальные или динамические переменные и объекты. Однако существует два ограничения на использование объектов MFC-классов. 1. Два потока не должны одновременно получать доступ к одному объекту MFC. Например, два потока могут использовать один объект класса CString при условии, что они не будут одновременно вызывать функцию класса CString. Чтобы запретить одновременный доступ к объекту, можно использовать один из методов синхронизации. Два потока могут одновременно получить доступ к разным объектам, принадлежащим даже одному MFC-классу, например, к двум объектам класса CString. 2. Объект любого из указанных ниже классов или объект класса, наследуемый от них, доступен только для потока, создавшего объект. CWnd CDC CMenu CGdiObject Эти классы имеют общее свойство: каждый из них хранит дескриптор для некоторых подчиненных объектов. Например, объект класса CWnd хранит деакриптор окна, который хранится в переменной m_hWnd объекта. Если поток создает объект одного из этих классов, то при получении доступа к подчиненному элементу из второго потока необходимо передать дескриптор, после чего второй поток должен создать свой собственный объект. Например, если поток создает объект класса CWnd для управления окном, а рисовать в этом окне требуется с помощью второго потока, то вместо передачи во второй поток объекта класса CWnd, ему передается дескриптор окна, хранящийся в переменной m_hWnd объекта. Второй поток затем вызывает функцию FromHandle класса CWnd, чтобы создать собственный объект этого класса CWnd. Управление запущенными потоками. После того как поток запущен, запустивший поток знает идентификатор дочернего потока. Пользуясь этим идентификатором, запустивший поток может управлять состоянием дочернего потока, изменяя его приоритет, приостанавливая или завершая его работу. Изменение приоритета потока Родительская задача может изменить относительный приоритет запущенной ею дочерней задачи с помощью функции SetThreadPriority BOOL SetThreadPriority( HANDLE hThread, //идентификатор задачи int nPriority); //новый уровень приоритета задачи Абсолютный уровень приоритета, который получит поток, зависит от класса приоритета процесса, который можно изменить при помощи функции SetPriorityClass. При работе с библиотекой MFC относительный приоритет потока можно увеличить, записав следующую строку PWinThread -> SetThreadPriority(THREAD_PRIORITY_ABOVE_NORMAL); В приведенном примере предполагается, что PwinThread содержит указатель типа класса CWinThread. Определение приоритета потока Для определения относительного приоритета задачи используется функция GetThreadPriority int GetThreadPriority(HANDLE hThread); Эта функция возвращает одно из значений относительного приоритета задача или значение THREAD_PRIORITY_ERROR_RETURN при возникновении ошибки. Приостановка и возобновление выполнения потока В некоторых случаях имеет смысл приостановить выполнение задачи. Например, если пользователь работает с многооконным приложением и для каждого окна запускается отдельная задача, для повышения производительности можно приостановить выполнение задач в неактивных окнах. Прототип API функции для приостановки выполнения задачи: DWORD SuspendThread(HANDLE hThread); Вызов функции SuspendThread класса CWinThread : PWinThread -> SuspendThread(); Для каждой задачи операционная система хранит счетчик приостановок, который увеличивается при каждом вызове функции SuspendThread Если значение этого счетчика больше нуля, задача приостанавливается. Для уменьшения значения счетчика приостановок и , соответственно, для возобновления выполнения задачи используется API функция: DWORD ResumeThread(HANDLE hThread); Вызов функции ResumeThread класса CWinThread : PWinThread -> ResumeThread(); В случае ошибки функции SuspendThread и ResumeThread возвращают значение 0xFFFFFFFF. Для временной приостановки задачи на заданный период времени используется API функция: VOID Sleep(DWORD cMilliseconds); Задача, выполняющая ожидание с помощью этой функции, не снижает производительности системы, т.к. ей не распределяются кванты времени. Если для времени ожидания указать нулевое значение, то при наличии в системе запущенных задач с таким же приоритетом, как у задачи, вызвавшей эту функцию, ожидающая задача отдаст оставшееся время от своего кванта другой задаче. В том случае, когда в системе нет других задач с таким же приоритетом, функция Sleep немедленно возвращает управление вызвавшей ее задаче, которая будет использовать остаток своего кванта времени. Есть еще одна возможность: можно организовать бесконечную задержку, передав функции Sleep значение INFINITE. Завершение потока Поток может завершиться: · по собственной инициативе · по инициативе другой задачи При завершении задачи по собственной инициативе можно выделить две ситуации: 1. Выполняется оператор возврата из функции задачи (return 0) Стек, используемый потоком, освобождается, и все автоматические данные, созданные потоком, удаляются, т.е. вызываются деструкторы для автоматических объектов. 2. Поток может вызвать функцию, передавая ей нужный код возврата. Это может быть MFC-функция: Void AfxEndThread (UINT nExitCode); Для задач, запущенных с помощью функции CreateThread, можно вызвать функцию: VOID ExitThread (DWORD dwExitCode); Это удобный способ завершения потока из вложенной функции (вместо возврата в исходную функцию). При этом стек освобождается, но деструкторы для автоматических объектов не вызываются. Для принудительного завершения дочерней задачи родительская задача может использовать API функцию TerminateThread, передав ей идентификатор завершаемой задачи и код завершения. Прототип функции: BOOL TerminateThread( HANDLE hThread, //идентиф-р завершаемой задачи DWORD dwExitCode); //код завершения Как родительская задача может получить код завершения дочерней задачи? Для этого должна быть вызвана функция: BOOL GetExitCodeThread( HANDLE hThread, //идентиф-р завершаемой задачи LPWORD lpExitCode); //адрес для приема кода завершения Если задача, для которой вызвана функция GetExitCodeThread все еще работает, вместо кода завершения возвращается значение STILL_ACTIVE. Если задача была запущена с помощью функций _beginthread или _beginthreadex, она может завершить свое выполнение только с помощью функций соответственно _endthread и _endthreadex. Функцию ExitThread в этом случае использовать нельзя, так как при этом не будут освобождены ресурсы, заказанные для работы с мультизадачным вариантом библиотеки времени выполнения. При управлении потоками, созданными с использованием MFC функции, возникает следующая проблема. После завершения потока объект класса CWinThread, поддерживаемый функцией AfxEndThread, автоматически удаляется. При попытке доступа к объекту класса после выхода из потока программа сгенерирует ошибку защиты. Однако чтобы определить, завершен ли поток, необходимо получить доступ к переменной m_hThread класса CWinThread. Решение этой проблемы состоит в предотвращении автоматического удаления объекта класса CWinThread при завершении потока. Для этого присвойте значение FALSE переменной m_bAutoDelete класса CWinThread, как в следующем примере. PWinThread = AfxBegiThread (ThreadFunction, &Code, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED); PWinThread->m_bAutoDelete = FALSE; PwinThreate->ResumeThread(); Поток запускается в состоянии ожидания, чтобы перед выходом из потока установить значение переменной m_bAutoDelete, равное FALSE. Если значение переменной m_bAutoDelete равно FALSE объект класса CWinThread будет существовать даже после выхода из потока, а программа сможет вызвать функцию GetExitCodeThread, чтобы определить, выполнился поток, и получить код его выхода. После окончания работы с объектом его можно удалить явно Delete PWinThread; Замечания по завершению потоков в поектах, использующих MFC классы 1. При необходимости завершить поток из другого потока рекомендуется передать из последнего сигнал в завершаемый поток, по которому он сам себя завершит. 2. Функция Win32 API ::GetExitCodeThread вызывается, чтобы определить, продолжается ли выполнение потока. Если выполнение завершено, позволяет получить код возврата. В первом параметре функции потока ::GetExitCodeThread необходимо задать дескриптор Windows, который хранится в переменной m_hThread класса CWinThread. int Code = 1; CWinThread *PWinThread; PWinThread = AfxBeginThread (ThreadFunction, &Code); //… DWORD ExitCode; ::GetExitCodeThread (PWinThread->m_hThread, //дескриптор Windows для потока; &ExitCode); //адрес переменной DWORD для //получения кода возврата if ExitCode ==STILL_ACTIVE) //поток продолжает выполнение… else //поток завершен и функция ExitCode содержит код возврата Если поток продолжает выполнение, то переменной ExitCode пристаивается значение кода возврата STILL_ACTIVE (0х00000103L). Если поток завершился- присваивается значение кода возврата. -------------------- Никто и никогда не должен решать одну проблему дважды |
|||
|
||||
| Step |
|
|||
|
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 5151 Регистрация: 26.9.2002 Где: дурдом.UA Репутация: 5 Всего: 25 |
Georg4 вот это ты расписал.
Уважаю. -------------------- - Дурак учится на своих ошибках, умный на чужих. - умные учатся у дураков |
|||
|
||||
| dim |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 106 Регистрация: 24.12.2002 Репутация: нет Всего: нет |
В 9x она тоже есть. --------------------
that's all |
|||
|
||||
| LZero |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 10 Регистрация: 27.2.2003 Репутация: нет Всего: нет |
Ctrl+C... Ctrl+V...
Скопировал, спасибо за инфу Georg4. |
|||
|
||||
![]()
|
| Правила форума "С++:Общие вопросы" | |
|
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |