Home | | Multi - Core Architectures and Programming | Creating Native Windows Threads

Chapter: Multicore Application Programming For Windows, Linux, and Oracle Solaris : Windows Threading

Creating Native Windows Threads

A basic Windows application will start with a single thread. The function call to request that Windows create a child thread is CreateThread().


Creating Native Windows Threads

 

A basic Windows application will start with a single thread. The function call to request that Windows create a child thread is CreateThread(). This call takes the parameters shown in Table 6.1.

 

Table 6.1      Parameters Passed to CreateThread()


 

The return value from the function call is a handle for the created thread, which is a different construct than the thread ID. Handles will be discussed in more detail later. A return value of zero means that the call was unsuccessful.

 

All of the parameters, with the exception of the address of the function to execute, will take sensible defaults if they are provided the null value. Listing 6.1 shows code to create a child thread using the CreateThread() call. The call to GetCurrentThreadId() will return an integer ID for the calling thread.

 

Listing 6.1     Creating a Thread Using a Call to CreateThread()

#include <Windows.h>

 

DWORD WINAPI mythread(__in LPVOID lpParameter)

 

{

 

printf( "Thread %i \n", GetCurrentThreadId() ); return 0;

 

}

 

int _tmain( int argc, _TCHAR* argv[] )

 

{

 

HANDLE handle;

 

handle = CreateThread( 0, 0, mythread, 0, 0, 0 );

 

getchar(); return 0;

 

}

 

If it is important to capture the ID of the created thread, the code shown in Listing 6.2 could be used. The thread ID is not very useful, because most functions take the thread handle as a parameter.

 

Listing 6.2     Capturing the ID of the Created Thread

int _tmain( int argc, _TCHAR* argv[] )

 

{

 

HANDLE handle;

 

DWORD threadid;

 

handle = CreateThread( 0, 0, mythread, 0, 0, &threadid ); printf( "Thread %i \n", threadid );

 

getchar(); return 0;

}

Calling CreateThread() tells the operating system to produce a new thread but does not set that thread up to work with the libraries provided by the developer envi-ronment. Windows essentially creates the thread and returns a handle to that thread.

However, the runtime libraries have not had the opportunity to set up the thread-local data structures that they need. In most instances, the libraries will create any structures that they need the first time that they are called, but not all library calls are able to do that. Therefore, it is recommended that instead of calling CreateThread(), the calls pro-vided by the runtime libraries are used. The two recommended ways of creating a thread are the calls _beginthread() and _beginthreadex(). The two functions take different parameters. Table 6.2 provides the parameters for _beginthread().

 

Table 6.2      Parameters Passed to _beginthread()

Parameter Type  :Comment

void(*)(void*)  : The address of the function that the thread will execute

unsigned int:   The stack size for the thread

void* :The pointer to the parameters that are to be passed to the thread

Table 6.3 shows the parameters for _beginthreadex(). These are the same as the parameters taken by CreateThread().

 

Table 6.3      Parameters Passed to _beginthreadex()


There is another difference between these two routines other than the parameters that they take. A thread created by a call to _beginthread() will close the handle to the thread when the thread exits. The handle returned by _beginthreadex() will have to be explicitly closed by the programmer by calling CloseHandle(). This requirement is similar to the concept of detached threads in POSIX.

 

The two functions also differ by the type of function that the thread will execute. _beginthread() is a void function and uses the default calling convention __cdecl, whereas _beginthreadex() returns an unsigned int and uses the __stdcall calling convention.

 

Both the _beginthread() and _beginthreadex() functions return handles to the newly created threads. However, the actual return type of the function call is uintptr_t, which has to be type cast to a HANDLE before it can be used in function calls that expect an object handle.

 

Listing 6.3 provides example code for creating threads using the three different approaches discussed. The call to WaitForSingleObject() waits for an object to signal its readiness; in this instance, the routine is passed the handle to a thread and waits for that thread to terminate.

 

Listing 6.3  Three Different Ways of Creating Threads

 

#include <windows.h> #include <process.h>

DWORD WINAPI mywork1( __in LPVOID lpParameter )

 

{

 

printf( "CreateThread thread %i\n", GetCurrentThreadId() );

 

return 0;

 

}

 

unsigned int __stdcall mywork2( void * data )

 

{

 

printf( "_beginthreadex thread %i\n", GetCurrentThreadId() );

 

return 0;

 

}

 

void mywork3( void * data )

 

{

 

printf( "_beginthread thread %i\n", GetCurrentThreadId() );

 

}

 

int _tmain( int argc, _TCHAR* argv[] )

 

{

 

HANDLE h1, h2, h3;

 

h1 = CreateThread( 0, 0, mywork1, 0, 0, 0 );

 

h2 = (HANDLE)_beginthreadex( 0, 0, &mywork2, 0, 0, 0 );

 

WaitForSingleObject( h2, INFINITE );

 

CloseHandle( h2 );

 

h3 = (HANDLE)_beginthread( &mywork3, 0, 0 );

 

getchar();

}

 

 

Although calling _beginthread() looks appealing because it takes fewer parameters and cleans up the handle after the thread exits, it is better to use _beginthreadex().

 

The call to _beginthreadex() avoids a difficulty with _beginthread(). If the thread terminates, the handle returned by the call to _beginthread() will be invalid or even reused, so it is impossible to query the status of the thread or even be confident that the handle to the thread is a handle to the same thread to which it originally pointed.

Listing 6.4 shows an example of this problem.

 

Listing 6.4     Code Where a Thread Handle Could Be Reused

#include <windows.h> #include <process.h>

 

void mywork1( void * data )

 

{

 

}

 

void mywork2( void * data )

 

{

 

volatile int i;

 

for (i=0; i<100000; i++)

 

{}  // because i is volatile most compilers will not // eliminate the loop

 

}

 

int _tmain( int argc, _TCHAR* argv[] )

 

{

 

HANDLE h1, h2;

 

h1 = (HANDLE)_beginthread( &mywork1, 0, 0 );

 

h2 = (HANDLE)_beginthread( &mywork2, 0, 0 );

 

WaitForSingleObject( h1, INFINITE );

 

WaitForSingleObject( h2, INFINITE );

 

}

The routine mywork1() in Listing 6.4 terminates quickly and may have already ter-minated by the time that the main thread reaches the call to create the second thread. If the first thread has terminated, the handle to the first thread may be reused as the handle to the second thread. Queries using the handle of the first thread might succeed, but they will work on the wrong thread. In the code shown in Listing 6.4, the calls to WaitForSingleObject() may not be using a correct or valid handle for either of the threads depending on the completion time of the threads.

 

Listing 6.5 shows an equivalent code that uses _beginthreadex(). Threads

 

created with _beginthreadex() need to be cleaned up with a call to CloseHandle(). Consequently, the calls to WaitForSingleObject() are certain to get the correct handles.

 

Listing 6.5 Creating Threads Using _beginthreadex() to Ensure That Handles Are Not Reused

#include <windows.h> #include <process.h>

 

unsigned int __stdcall mywork1( void * data )

 

{

 

return 0;


}

 

unsigned int __stdcall mywork2( void * data )

 

{

 

volatile int i;

 

for (i=0; i<100000; i++)

 

{}  // because i is volatile most compilers will not // eliminate the loop

 

return 0;

 

}

 

int _tmain( int argc, _TCHAR* argv[] )

 

{

 

HANDLE h1, h2;

 

h1 = (HANDLE)_beginthreadex( 0, 0, &mywork1, 0, 0, 0);

 

h2 = (HANDLE)_beginthreadex( 0, 0, &mywork2, 0, 0, 0);

 

WaitForSingleObject( h1, INFINITE );

 

WaitForSingleObject( h2, INFINITE );

 

CloseHandle( h1 );

 

CloseHandle( h2 );

}

 

Study Material, Lecturing Notes, Assignment, Reference, Wiki description explanation, brief detail
Multicore Application Programming For Windows, Linux, and Oracle Solaris : Windows Threading : Creating Native Windows Threads |


Privacy Policy, Terms and Conditions, DMCA Policy and Compliant

Copyright © 2018-2024 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.