Передача потока подпрограммы несколько переменных и размер стека - PullRequest
1 голос
/ 04 июня 2011

В примерах в Интернете вызовы 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 %f\n", 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)&p,
    0,  // ?? I think I should be using this parameter</b>
    &threadId
  ) ;

  if( !handle )
  {
    // Thread creation failed
    puts( "start fail\n" );
  }
  else
  {
    printf( "started on threadid=%d\n", 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 в процедуру запуска потока:

DWORD threadStartPointFuncStyleIWant( Data d ) ;

Так что мои вопросы на самом деле:

  • Для начальной точки потока (CreateThread) мы ограничены функцией с прототипом вида:
DWORD validThreadFunc( LPVOID pParamStruct ) ;
  • Или мы можем запустить поток для таких функций, как
DWORD threadFunc1( int p1, int p2 ) ;
DWORD threadFunc2( Data d ) ;

Ответы [ 3 ]

7 голосов
/ 04 июня 2011

CreateThread принимает только один тип функции, который принимает один параметр LPVOID. Нет никакого способа связаться с kernel32.dll, который является кодом, который вызывает вашу функцию потока, что он должен вызывать ее с любым другим списком параметров. Ядро всегда вызывает функцию одинаково.

Вы просто должны будете продолжать делать то же самое, что и все остальные. Выделите структуру в куче, передайте указатель на подпрограмму потока, а затем освободите ее, прежде чем вернуться из подпрограммы потока. Таким образом, право собственности переходит от исходного потока к новому потоку. Как только новый поток становится владельцем, вы можете использовать умный указатель, такой как shared_ptr, чтобы убедиться, что он освобождается, как только вы покидаете поток.

Вы можете передать другой указатель на функцию в этой структуре или передать объект с собственными методами. Тогда ваша процедура потока становится не чем иным, как распаковкой параметра LPVOID и отправкой указателю или методу другой функции, где вы выполняете всю реальную работу для потока.

2 голосов
/ 04 июня 2011

Выполнение того, что вы хотите, хотя и не невозможно, потребует такого большого сотрудничества между компилятором (ами) и ОС, что цели проекта ОС будут поставлены под угрозу.выделите стек и инициализируйте его так, чтобы верхняя часть фрейма стека выглядела так, как если бы поток выполнялся ранее и был прерван.Чтобы запустить поток, выполняющийся в первый раз, ОС затем выполняет возврат прерывания, т.е.потоки никогда не вызываются ОС, они всегда возвращаются.Чтобы обеспечить переменный формат параметра, ОС должна знать о длине блока параметров, чтобы она могла скопировать параметры из стека вызывающего потока в стек нового потока до того, как будет вставлен кадр прерывания.Вы видите, как грязно это становится?Что произойдет, если поток ctor и новый поток имеют разные соглашения о вызовах?

Проще / безопаснее просто передать один параметр указателя, который можно «передать» в регистре.В случае ОО-языков, таких как C ++, это будет «this», чтобы новый поток мог получить доступ к своим собственным элементам данных и указателю vtable.

Rgds, Martin

0 голосов
/ 04 июня 2011

Вы можете использовать библиотеку потоков Boost в дополнение к вызову boost :: bind, чтобы получить что-то, что вы хотите:

#include <boost\thread.hpp>

struct Point
{
    float x,y,z;
};

void threadStartPoint( Point p )
{
  Sleep( 1000 ) ;
  printf( "%f %f %f\n", 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;
}

Будьте очень осторожны, чтобы не передавать указатели, исходящие из стека, в порождающую нить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...