[Windows core programming] windows core programming-thread Basics

Source: Internet
Author: User
Tags microsoft c

1. Thread

Like the process described earlier, the thread is composed of two parts:

1) A thread kernel object that the operating system uses to manage threads. The kernel object also stores various statistical information about threads, including the pending count and exit code, so that the system can manage threads. The kernel object has a context structure that stores the status of the CPU registers during the last thread execution.

2) A thread stack is used to maintain all function parameters and local variables required for thread execution.

The threads in the same process share the process'sAddress SpaceAnd they share the processHandle table. Because the handle table is for processes. The process requires a lot of system resources, and the thread only needs oneThread Kernel Object and thread StackSo the thread overhead is much smaller than the process. It is also a matter of course that multithreading is used to solve the problem.
 
Multithreading can improve program execution efficiency, but multithreading also has many problems. When you try to use multiple threads, a new problem may be introduced. Such as synchronization issues.
 

2. Compile the first thread function

Each thread needs an entry point function. This is the starting point of thread execution. The entry point function of the main thread is _ tmain or _ twinmain. If you create a thread in a process, you must provide your own entry point function.

Shape:

DWORD winapi threadfunc (pvoid pvparam ){
DWORD dwresult = 0;
...
Return (dwresult );
}

A thread function can be any task we want it to execute. The final thread function will terminate and return. Similar to the process kernel object, if the thread kernel object uses the count to 0, it will be destroyed.

1) by default, the main thread's entry point function must be named main, wmain, winmain, or wwinmain. You can specify another function as the entry point function by setting the/entry: Linker option.
 
2) The main thread entry point function has string parameters, So it provides the ANSI/Unicode version. On the contrary, a thread function has only one parameter, which can be defined by us. You can either pass a value or use it as a pointer to a data structure. This requires type conversion within the thread function.

3) The thread function must return a value.Its value is passed to exitthread as the exit code of the thread.

4)Thread functions use local variables or function parameters whenever possibleThey are created on the thread stack.It is unlikely to be damaged by other threads.. When static or global variables are used, other threads can access these variables, which leads to synchronization and mutex issues.


Iii. createthread Function

To create one or more auxiliary threads, you only need to have a running thread call createthread:

Handle createthread (

Psecurity_attributes PSA, // pointer to the security_attributes structure;

DWORD cbstacksize, // specifies the address space that a thread can use for its thread stack;Stack space size. If 0 is passed in, internal value linker/stack can be used by default to change the size by 1 MB.

Pthread_start_routine pfnstartaddr, // address of the thread function;

Pvoid pvparam, // thread function parameters

DWORD dwcreateflags, // specify an additional flag to control thread creation; create_suincluded pause execution

Pdword pdwthreadid); // The ID assigned by the storage system to the new thread;

Example:

DWORD winapi firstthread (pvoid pvparam)

{

Int x = 0; // The function here is the return value of secoundthread. It is best to declare it as static to prevent the access Stack from going out of bounds when the firstthread is executed.

DWORD dwthreadid;

Handle hthread = creatthread (null, 0, secondthread, (pvoid) & X, 0, & dwthread );

}

DWORD winapi secondthread (pvoid pvparam)

{


* (Int *) pvparam = 5;

}

When createthread is called, The system createsThread Kernel Object. System slaveProcess address spaceAllocate memory to the thread stack. New Threads can accessAll handles of process kernel objects, In the processAll memoryAnd all otherThread Stack.

The Windows function used to create a thread when the createthread function is used. However, if you are writing C/C ++ code, do not call createthread. The correct choice is to use the Microsoft C ++ runtime function _ beginthreadex. If you are not using the Microsoft C ++ compiler, your compiler provider should provide similar functions to replace createthread. No matter what the function is, you must use it.

4. Terminate the running thread

The thread can terminate the operation in the following four ways:

Thread function return (this is strongly recommended );

The thread uses the exitthread function to "kill" itself (should be avoided );

The terminatethread function is called by a thread in the same process or another process (should be avoided );

Processes with threads should be terminated (should be avoided );

5. thread function return

When designing thread functions, make sure that they are returned when we want the threads to terminate the operation. This is the only way to ensure that all resources of the thread are correctly cleared. Let the thread function return to ensure that all the right work of the application is executed correctly:

All C ++ objects created in thread functions are correctly destroyed through their destructor;

The operating system correctly releases the memory used by the thread stack;

The operating system sets the exit code of the thread (maintained in the kernel object of the thread) as the return value of the thread function;

The system reduces the number of kernel objects used by threads;

Vi. exitthread Functions 

Void exitthread (DWORD dwexitcode );

This function terminates the execution of the thread and causes the operating system to clear all system resources used by the thread. However, your C/C ++ resources (such as C ++ class objects) will not be destroyed. So the better way isReturn directly from the thread function,Do not call exitthread by yourself.

Exitthread is a function used by windows to "kill" a thread. To write C/C ++ code, do not call exitthread. Instead, use the C ++ Runtime library function _ endthreadex. If Microsoft's c ++ compiler is not used, the compiler vendor should provide their own exitthread substitution functions. No matter what the alternative function is, you must use it.

7. terminatethread Function 

Bool terminatethread (

Handle hthread,

DWORD dwexitcode );

Terminatethread is an asynchronous function. When the exitthread function terminates a thread, the thread stack is destroyed, and the terminatethread function does not destroy the stack of the thread unless the process that owns the thread stops running.

8. When the thread stops running

When a thread is terminated, the system will perform the following operations at a time:

All user object handles owned by the thread will be released;

The exit code of the thread changes from stile_active to the Code passed to the exitthread or terminatethread function;

The state of the thread kernel object changes to the triggered state;

If the thread is the last active thread in the process, the system determines that the process is terminated;

The usage count of the thread kernel object is reduced by 1;
9. Thread insider

A call to the createthread function causes the system to create a thread kernel object. The initial usage count of this object is 2. (Create a thread kernel object plus 1 and return the thread kernel object handle plus 1). Therefore, unless the thread is terminated and the handle returned by createthread is disabled, the thread kernel object will not be destroyed. Other attributes of this thread object are also initialized: The pause count is set to 1, the exit code is device stile_active (0x103), and the object is set to not triggered.
When a kernel object is created, the system allocates memory for the thread stack. The memory is allocated from the address space of the process because the thread does not have its own address space. The system writes future values to the top of the new thread stack, as shown in 1, that is, the called thread function and its parameters.

Each thread has its own group.The CPU register is called the thread context.(Context ). Context reflectsThe status of the CPU register of the thread during the last thread execution. The context structure is stored in the kernel object of the thread.

When the thread kernel object is initialized, the stack pointer register of the context structure is set to the address of pfnstartaddr in the thread stack. The instruction pointer register is set to the address of the rtluserthreadstart function.

Void rtluserthreadstart (pthread_start_routine pfnstartaddr, pvoid pvparam ){
_ Try {
Exitthread (pfnstartaddr) (pvparam ));
}
_ Response T (unhandledexceptionfilter (getexceptioninformation ())){
Exitprocess (getexceptioncode ());
}
// Note: we never get here.
}

After the thread is fully initialized, the system checks whether the create_suincluded mark has been passed to the createthread function. If this flag is not passed, the system will decrease the number of pending threads to 0. Then, the thread can be scheduled to a processor for execution. Then, the system loads the value stored in the thread context in the actual CPU register. Now, the thread can execute code and process data in the address space of its process.

When the new thread executes the rtluserthreadstart function, the following occurs:

A structured exception handling (seh) frame is set around the thread function. In this way, any exceptions generated during thread execution can be processed by the system by default.

The system calls the thread function and passes the pvparam parameter passed to the createthread function to it.

When a thread function returns, rtluserthreadstart calls exitthread and passes the return value of your thread function to it. The usage count of the thread Kernel Object decreases, and then the thread stops running.

If the thread generates an unhandled exception, the seh frame set by the rtluserthreadstart function will handle this exception. Generally, this means that the system will display a message box to the user, and when the user closes the message box, rtluserthreadstart will call exitprocess to terminate the real process, rather than terminating the problematic thread.

When the main thread of a process is initialized, its command Pointer Points to rtluserthreadstart. When rtluserthreadstart starts execution, it calls the startup code of the C/C ++ Runtime Library, the latter initializes and calls your _ tmain or _ twinmain functions.

10. Precautions for running the C/C ++ Database

To ensure the normal operation of C and C ++ multi-threaded applications, you must create a data structure and associate it with each thread that uses the C/C ++ Runtime library function. Then, when calling the C/C ++ Runtime library function, those functions must know how to find the data blocks of the main thread to avoid affecting other threads.

Write C/C ++ applications,Do not call the createthread function of the operating system., On the contrary,Call the C/C ++ Runtime library function _ beginthreadex:

 uintptr_t __cdecl _beginthreadex (  void *psa, unsigned cbStackSize,  unsigned (__stdcall * pfnStartAddr) (void *),                  void * pvParam,  unsigned dwCreateFlags,  unsigned *pdwThreadID)    {                  _ptiddata     ptd;         // Pointer to thread's data block                  uintptr_t      thdl;         // Thread's handle                  // Allocate data block for the new thread.                if ((ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL)                        goto   error_return; // Initialize the data block.               initptd(ptd);             // Save the desired thread function and the parameter               // we want it to get in the data block.               ptd->_initaddr = (void *) pfnStartAddr;               ptd->_initarg = pvParam;               ptd->_thandle = (uintptr_t)(-1); // Create the new thread.               thdl = (uintptr_t) CreateThread ((LPSECURITY_ATTRIBUTES)psa, cbStackSize, _threadstartex , (PVOID)         ptd, dwCreateFlags, pdwThreadID);               if (thdl == 0) {                         // Thread couldn't be created, cleanup and return failure.                        goto error_return;              } // Thread created OK, return the handle as unsigned long.        return(thdl);       error_return:          // Error: data block or thread couldn't be created.         // GetLastError() is mapped into errno corresponding values         // if something wrong happened in CreateThread.         _free_crt(ptd);        return((uintptr_t)0L);     } 

The _ beginthreadex function has the following key points:

1) Each thread has its own dedicated _ tiddata memory block, which is allocated from the heap of the C/C ++ Runtime Library.

2) the address of the thread function passed to _ beginthreadex is saved in the _ tiddata memory block.

3) _ beginthreadex will indeed call createthread internally, because the operating system only knows to use this method to create a new thread.

4) when the createthread function is called, the address of the function passed to it is _ threadstartex (rather than pfnstartaddr ). In addition, the parameter address is the address of the _ tiddata structure, rather than pvparam.

5) if everything goes well, the thread handle will be returned, just like createthread. If any operation fails, 0 is returned.

After initializing the _ tiddata structure for the new thread, let's take a look at how this structure is associated with the thread:

Static unsigned long winapi _ threadstartex (void * PTD ){

// Note: PTD is the address of this thread's tiddata block.
// Associate the tiddata block with this thread so

// _ Getptd () will be able to find it in _ callthreadstartex.

Tlssetvalue (_ tlsindex, PTD );
// Save this thread ID in the _ tiddata block.
(_ Ptiddata) PTD)-> _ tid = getcurrentthreadid ();

// Initialize floating-point Support (Code not shown ).

// Call helper function.
_ Callthreadstartex ();
// We never get here; the thread dies in _ callthreadstartex.
Return (0l );
}

Static void _ callthreadstartex (void ){
_ Ptiddata PTD;/* pointer to thread's _ tiddata struct */

// Get the pointer to thread data from TLS
PTD = _ getptd ();

// Wrap desired thread function in seh frame
// Handle run-time errors and signal support.
_ Try {
// Call desired thread function, passing it the desired parameter.
// Pass thread's exit code value to _ endthreadex.
_ Endthreadex (
(Unsigned (winapi *) (void *) (_ ptiddata) PTD)-> _ initaddr ))
(_ Ptiddata) PTD)-> _ initarg ));
}
_ Effect (_ xcptfilter (getexceptioncode (), getexceptioninformation ())){

// The C Run-Time's exception handler deals with run-time errors

// And signal support; we shoshould never get it here.

_ Exit (getexceptioncode ());
}
}
For the _ threadstartex function, pay attention to the following points:

1) The new thread first executes rtluserthreadstart (in the ntdll. dll file) and then jumps to _ threadstartex;

2) the unique parameter _ threadstartex is the address of the _ tiddata memory block of the new thread;

3) tlssetvalue is an operating system function that associates a value with the main function. This is the so-called Thread Local Storage (TLS ). The _ threadstartex function associates the _ tiddata memory block with the new thread;
4) in the auxiliary function _ callthreadstartex without parameters, there is a seh frame, which will enclose the expected thread function to be executed. This frame processes many tasks related to the Runtime Library, such as runtime errors;

5) The expected thread function to be executed will be called and the expected parameters will be passed to it. The address and parameters of the function are saved in the _ tiddata data block of TLS and obtained from the TLS in _ callthreadstartex;
6) The return value of the thread function is considered as the exit code of the thread;

Let's take a look at _ endthreadex:

 

void __cdecl _endthreadex (unsigned retcode) {     _ptiddata ptd; // Pointer to thread's data block     // Clean up floating-point support (code not shown).     // Get the address of this thread's tiddata block.     ptd = _getptd_noexit ();     // Free the tiddata block.     if (ptd != NULL)         _freeptd(ptd);     // Terminate the thread.     ExitThread(retcode);     } 

For the _ endthreadex function, pay attention to the following points:

1) The _ getptd_noexit function of the C Runtime Library calls the tlsgetvalue function of the operating system internally, and the latter obtains the address of the tiddata memory block of the main function;
2) then _ endthreadex releases the data block and calls the exitthread function of the operating system to actually destroy the thread. It passes and correctly sets exit code;

We should avoid using the exitthread function, because this function will "kill" the main thread and will not allow it to return from the currently executed function. Because no function is returned, any c ++ object constructed will not be destructed; it will also prevent the thread's _ tiddata memory block from being released, expose the application memory (until the entire process is terminated ).
We should try to use C/C ++ to run library functions (_ beginthreadex, _ endthreadex) and avoid using the functions provided by the operating system (createthread, exitthread ).

11. Worry about pseudo handles

Handle getcurrentprocess ();

Handle getcurrentthread ();

These two functions return a pseudo handle to the process kernel object or thread Kernel Object of the main call function. They will not create new handles in the handle table of the main process. That is, the returned handle does not have actual table items in the Process Handle table, nor does it affect the count of the Process kernel objects or thread kernel objects. If the handler uses the closehandle function and passes in a "pseudo handle", closehandle simply ignores this call.
To convert a pseudo handle to a real handle, the duplicatehandle function can perform this conversion. For example, to create a thread Kernel Handle in the Process Handle table:

    DuplicateHandle(            GetCurrentProcess(),     // Handle of process that thread                                                 // pseudohandle is relative to            GetCurrentThread(),    // Parent thread's pseudohandle            GetCurrentProcess(),  // Handle of process that the new, real,                                                 // thread handle is relative to             &hThreadParent,        // Will receive the new, real, handle                                              // identifying the parent thread             0, // Ignored due to DUPLICATE_SAME_ACCESS             FALSE, // New thread handle is not inheritable             DUPLICATE_SAME_ACCESS); // New thread handle has same 


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.