3.2.1 Creating Threads

It is useful for a process to have multiple threads in situations where the application has several tasks that need to execute asynchronously. For example, an application that opens more than one window could have a separate thread to perform the work of the application in each window. Other examples are an application that manages input from several communications devices; or a process that connects to a named pipe and then uses one thread to write to the pipe and another thread to monitor the pipe for incoming messages.

The CreateThread function creates a new thread for a process. Arguments to CreateThread specify the starting address of the code that the thread is to execute, and optionally, a single 32-bit pointer argument. Typically the starting address is the name of a function defined in the program's code. The CreateThread call may succeed even if this address points to data, code, or is not accessible. When the thread executes, if the start address is bad, an exception will occur and the thread will terminate. The thread function can have a single pointer argument and it returns a DWORD result. The following code fragment illustrates the creation of a new thread that will execute the ThreadFunc function:

DWORD ThreadFunc(LPDWORD param)

{

printf("ThreadFunc: param=%d\n", *param);

return 0;

}

DWORD main(void)

{

DWORD dwThreadId, dwThrdParam = 1, dwWaitResult;

HANDLE hThread;

hThread = CreateThread(NULL, /* no security attributes */

0, /* default stack size */

ThreadFunc, /* thread function */

&dwThrdParam, /* argument to the thread functions */

0, /* default creation flags */

&dwThreadID); /* returns thread id */

if (!hThread) return 1;

.

.

.

}

For simplicity this example passes a pointer to a locally declared variable as an argument to the thread function. This could have been a pointer to any type of data or structure, or it could have been omitted altogether by passing a NULL pointer and deleting the references to the parameter in ThreadFunc . A danger in passing the address of local variable occurs if the creating thread exits before the new thread, in which case the thread's pointer would be invalid. Consequently, you should either pass dynamically allocated parameters or have the creating thread wait for the new thread to terminate. Data can also be passed from the creating thread to the new thread using global variables. Using the thread function's argument to pass data to the new thread is particularly useful in applications where the main thread might create multiple threads to execute the same code, in which case, it is inconvenient to use global variables. For example, an application that allows the user to open several files at the same time could create a new thread for each file, with each of the threads executing the same thread function. The creating thread would pass as an argument the unique information (such as the file name) needed by each instance of the thread function.

Other arguments to the CreateThread function include an optional pointer to an attributes structure, an argument specifying the stack size of the new thread, and a creation flags argument.

The attributes argument to CreateThread is a pointer to a SECURITY_ATTRIBUTES structure that contains a handle inheritance flag and a pointer to a security descriptor structure for the thread object. The inheritance flag determines whether the handle to the new thread can be inherited by child processes. If the attributes argument is a NULL pointer, the thread handle can not be inherited. If the attributes argument is a NULL pointer, or if its security descriptor pointer is NULL, the handle returned by CreateThread will have full access to the new thread. Otherwise, the handle's access will be as specified in the security descriptor.

The stack size argument to CreateThread can be used to specify the size in bytes of the new thread's user stack. If 0 is specified, the stack size defaults to the same size as that of the main thread. In either case, the stack is allocated automatically in the memory space of the process, and it is freed when the thread terminates.

CreateThread returns two identifiers for the new thread. A handle to the new thread is the return value of the function; and the id of the thread is returned in the function's ThreadID argument. The thread handle has the access rights specified in the security descriptor, or full access by default. It can be inherited by other processes, if the inherit flag in the attributes argument was set. If the handle's access is appropriate, it can be used to identify the thread in any of the following API that access the thread object: SetThreadPriority, GetThreadPriority, TerminateThread, GetExitCodeThread, GetThreadSelectorEntry, GetThreadContext, SetThreadContext, SuspendThread, ResumeThread. It can also be used in WaitForSingleObject or WaitForMultipleObjects to wait for the thread to terminate.