#c #windows #multithreading
#c #Windows #многопоточность
Вопрос:
В примерах в Интернете вызовы CreateThread обычно передают указатель на структуру для LPVOID lpParameter
, и вы используете этот указатель для доступа к самой структуре.
#include <Windows.h>
#include <stdio.h>
struct Point
{
float x,y,z ;
} ;
DWORD WINAPI threadStartPoint( LPVOID data )
{
Sleep( 1000 ) ;
Point *p = (Point*)data ;
printf( "%f %f %fn", p->x, p->y, p->z ) ;
puts( "Thread job done" ) ;
return 0 ;
}
// From main
int main()
{
DWORD threadId ;
Point p ;
p.x=2, p.y=3, p.z=4 ;
HANDLE handle = CreateThread( 0, 0,
threadStartPoint,
(LPVOID)amp;p,
0, // ?? I think I should be using this parameter</b>
amp;threadId
) ;
if( !handle )
{
// Thread creation failed
puts( "start failn" );
}
else
{
printf( "started on threadid=%dn", threadId ) ;
}
WaitForSingleObject( handle, 2000 ) ; // wait up to 2000 ms for the other thread to complete before moving on
puts( "main thread Exiting.." ) ;
//system( "pause" ) ;
}
Я нахожу это в некоторой степени неудобным, поскольку вы должны убедиться, что struct существует, и убедиться, что он должным образом уничтожен, когда поток завершает выполнение.
Я хотел бы запустить свой поток, но передаю обычные аргументы стека, т. Е. автоматические переменные, или, возможно, struct
саму процедуру запуска потока:
DWORD threadStartPointFuncStyleIWant (Общий доступ к данным) ;
Итак, мои вопросы действительно:
- Для начальной точки потока (CreateThread) ограничены ли мы функцией с прототипом формы:
DWORD validThreadFunc(LPVOID pParamStruct ) ;
- Или мы можем запустить поток для таких функций, как
DWORD threadFunc1(int p1, int p2) ; DWORD threadFunc2(Data d) ;
Ответ №1:
CreateThread
принимает только один тип функции, тот, который принимает один LPVOID
параметр. Нет способа связаться с kernel32.dll это код, который вызывает вашу потоковую функцию, что он должен вызывать ее с любым другим списком параметров. Ядро всегда вызывает функцию одним и тем же способом.
Вам просто нужно продолжать делать это так же, как и все остальные. Выделите структуру в куче, передайте указатель вашей процедуре потока, а затем освободите ее перед возвратом из вашей процедуры потока. То есть право собственности переходит от исходного потока к новому потоку. Как только новый поток получит право собственности, вы можете использовать интеллектуальный указатель, подобный shared_ptr
, чтобы убедиться, что он освобождается, как бы вы ни покидали поток.
Вы можете передать другой указатель на функцию в этой структуре или передать объект с собственными методами. Тогда ваша потоковая процедура становится ничем иным, как чем-то для распаковки LPVOID
параметра и отправки другому указателю на функцию или методу, где вы выполняете всю реальную работу за поток.
Ответ №2:
Выполнение того, что вы хотите, хотя и не является невозможным, потребовало бы такого интенсивного сотрудничества между компилятором / -ами и ОС, что цели проектирования ОС были бы поставлены под угрозу.
Чтобы создать поток, ОС должна выделить стек и инициализировать его так, чтобы верхняя часть фрейма стека выглядела так, как будто поток выполнялся ранее и был прерван. Чтобы запустить поток, выполняющийся в первый раз, ОС затем выполняет возврат прерывания, т.Е. потоки никогда не вызываются операционной системой, они всегда возвращаются. Чтобы предоставить формат параметра, который является переменным, операционной системе необходимо было бы знать о длине блока параметров, чтобы она могла скопировать параметры из стека вызывающего потока в стек нового потока перед запуском фрейма прерывания. Вы видите, каким беспорядком это становится? Что произойдет, если поток ctor и новый поток имеют разные соглашения о вызовах?
Проще / безопаснее просто передать один параметр указателя, который может быть «передан» в регистре. В случае OO-языков, таких как C , это было бы «this», чтобы новый поток мог получить доступ к своим собственным элементам данных и указателю vtable.
Rgds, Мартин
Ответ №3:
Вы могли бы использовать библиотеку потоков Boost в дополнение к вызову boost::bind, чтобы получить что-то вроде того, что вы хотите:
#include <boostthread.hpp>
struct Point
{
float x,y,z;
};
void threadStartPoint( Point p )
{
Sleep( 1000 ) ;
printf( "%f %f %fn", p.x, p.y, p.z ) ;
}
int main(int argc, char** argv)
{
Point p;
p.x = 1;
p.y = 2;
p.z = 3;
// start the thread.
// first argument to "bind" is the worker thread function.
boost::thread t(boost::bind(threadStartPoint, p));
// wait for thread to exit
t.join();
return 0;
}
Будьте очень осторожны, чтобы не передавать указатели, исходящие из стека, в порождающий поток.