开发者

Passing thread routine multiple variables & stack size

开发者 https://www.devze.com 2023-03-11 11:27 出处:网络
In examples on the web, calls to CreateThread typically pass a pointer to a struct for LPVOID lpParameter, and you use that pointer to access the struct itself.

In examples on the web, calls to CreateThread typically pass a pointer to a struct for LPVOID lpParameter, and you use that pointer to access the struct itself.

#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" ) ;
}

I'm finding this to be somewhat of an inconvenience, since you have to make sure that struct exists, and make sure it is properly destroyed when the thread is finished executing.

I'd like to start my thread,开发者_运维百科 but passing normal stack arguments ie automatic variables, or perhaps, the struct itself to the thread start routine:

DWORD threadStartPointFuncStyleIWant( Data d ) ;

So my questions are really:

  • For a thread start point (CreateThread), are we restricted to a function with prototype of the form:
DWORD validThreadFunc( LPVOID pParamStruct ) ;
  • Or can we start a thread on functions like
DWORD threadFunc1( int p1, int p2 ) ;
DWORD threadFunc2( Data d ) ;


CreateThread accepts only one type of function, the one that takes a single LPVOID parameter. There's no way to communicate to kernel32.dll, which is the code that calls your thread function, that it should call it with any other parameter list. The kernel always calls the function the same way.

You'll just have to continue doing it the same way everyone else does. Allocate the structure on the heap, pass the pointer to your thread routine, and then free it before returning from your thread routine. That is, ownership passes from the originating thread to the new thread. Once the new thread gets ownership, you can use a smart pointer like shared_ptr to make sure it gets freed however you leave the thread.

You're welcome to pass another function pointer in that struct, or pass an object with methods of its own. Then your thread procedure becomes nothing more than something to unpack the LPVOID parameter and dispatch to the other function pointer or method, where you do all the real work for the thread.


Doing what you want, though not impossible, would require so much cooperation between the compiler/s and OS that the design goals of the OS would be compromised.

To create a thread, the OS has to allocate a stack and initialize it so that the top of the stack frame looks like the thread has been running before and been interrupted. To start the thread executing for the first time, the OS then performs an interrupt-return, ie. threads are never called by the OS, they are always returned to. To provide a parameter format that is variable, the OS would need to know about the parameter block length so it could copy the parameters from the calling thread stack to the stack of the new thread before the interrupt-frame is pushed on. You see how messy this is becoming? What happens if the ctor thread and new thread have different calling conventions?

Easier/safer to just pass one pointer parameter that can be 'passed' in a register. In the case of OO languages like C++, this would be 'this' so that the new thread can access its own data members and vtable pointer.

Rgds, Martin


You could use the Boost thread library in addition to the boost::bind call to get something like you want:

#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;
}

Be very careful not to pass pointers originating off the stack to the spawning thread.

0

精彩评论

暂无评论...
验证码 换一张
取 消