Why multithreading?
Multithreading is not necessarily the best, the right is the best.
The main advantage of multithreading is cheap, start fast, quit fast, share core objects with other threads, it is easy to realize the great dream of Communism. However, it has the disadvantage of unpredictable and difficult to test.
Using a lot of threads is to know when to use multithreading and when not to use them. If you should use multithreading, how to solve race condition problem? How do I share data? How to improve efficiency? How do I synchronize Threads and data? To sum up is:
- The creation and release of threads depends on their own
- Do not abandon do not give up, wait for a thread, let it finish their work
- Civilized and orderly, no conflict of resource occupancy
However, sometimes it is not recommended to use multithreading:
- Overlapped I/O is more capable for slow I/O devices
- The robustness of the program is very demanding, it is worth paying more than the extra burden, multi-process may be more competent
Manipulating threads How do I create a thread?
If you want to write a multithreaded program, the first step is to create a thread, we can use the CreateThread API function, you can also use the _beginthreadex C function, in fact, most of the time I use the Boost library above: Thread object to create a threading object. If you are interested in the boost library, don't talk about the boost library thread for the moment.
If you use the above two functions, you can go to MSDN to see. Using the above two functions to create a thread, its thread function must conform to the following format, of course, the function name can be replaced:
DWORD WINAPI ThreadFunc(LPVOID n);
Using the CreateThread API function or the _BEGINTHREADEX function, you can return two values to identify a new thread-the return value handle (handle) and the output parameter Lpthread (thread ID). For security reasons, it is not possible to get its handle based on the ID of a thread.
How do I release a thread?
threads, like processes, are core objects. How to release a thread that is part of how to release a core object. The CloseHandle function plays a very important role here. The function of the CloseHandle function is to reduce the reference count of the core object by 1. it cannot be used directly to release the core object, and the core object is automatically destroyed by the operating system only when its reference count is 0.
BOOL CloseHandle(HANDLE hObject);
If you do not call this function, the thread cannot be destroyed, even if the thread has finished executing after it was created and the reference count is not 0. If a process does not call CloseHandle on the core object it opens before the end, the operating system automatically cuts the reference count of those objects by one. Although the operating system will do the work, but he does not know the actual meaning of the core objects, it is impossible to know whether the deconstruction sequence is important. If you create a core object in the loop structure without closehandle, okay! You may have hundreds of thousands of handles that are not closed and your system will therefore not have a handle available, and then various anomalies appear. remember that when you are done with your work, you should call the CloseHandle function to release the core object.
You should also be aware of this issue when cleaning up core objects produced by threads. do not rely on the end of the thread to clean up all the core objects that are generated by this process. It is important to differentiate between an open object and its owner as a process or thread. This determines when the system will do the cleanup work. Programmers cannot choose to have processes or threads owning objects, and everything depends on the type of object. If the core object that is opened by the thread is owned by the process, the thread ends up being unable to clean up the core objects.
Thread Core objects and threads
In fact, these two are different concepts. The handle returned by the CreateThread function actually points to the thread core object, not directly to the thread itself. When a new thread is created, the thread itself turns on the thread core object, the reference count plus 1,createthread function returns a thread core object handle, and the reference count adds 1, so the thread core object starts with a reference count of 2.
Call the CloseHandle function, which is the core object reference count minus one, and after the thread execution completes, the reference count is reduced by one to zero, and the core object is automatically destroyed.
End Main thread
The first thing you need to know is which thread is the main thread: the one that executes after the program starts. The main thread has two features:
- Responsible for GUI main message loop
- When the main thread ends, forcing all other threads to end, other threads have no chance to perform cleanup work
The second feature means that if you do not wait for other threads to end, they will not have the opportunity to perform their own operations and have no chance to do the final cleanup operation. I met a situation where the program ran out because there was no waiting. It's dangerous anyway.
End thread and get its end code
This is nothing to say, you can use the ExitThread function to exit the thread and return a closing code. The GetExitCodeThread function gets the end code returned by the ExitThread function or return statement. But to wait for the thread to end through GetExitCodeThread is a bad note--cpu wasted. The WaitForSingleObject mentioned in the next section is the right path.
Terminating other threads
Terminating other threads can use the TerminateThread () function, or you can use a global tag.
The disadvantage of the TerminateThread () function is:
1, the thread does not have the opportunity to clean up before the end, its stack is not released, there is a memory leak;
2. Any DLLs that has an attachment relationship with this thread also has no chance to get thread cancellation notification;
3. The critical section that the thread enters will always be locked (the mutex will return the wait_abandoned state).
4, line is impersonating in the processing of data will be in an unstable state.
TerminateThread () The only thing that can be expected is that the thread handle becomes the firing state and returns the end code specified by Dwexitcode.
Advantages of setting up a global tag: Ensure that the target thread is in a secure and consistent state before it ends
Disadvantages of setting up a global tag: the thread needs a polling mechanism to check the tag value at all times. (You can use a manually reset event object )
Wait for a thread Wait for the end of a thread
The most obvious benefit of using WaitForSingleObject is that you can finally streamline the following code into one sentence.
for(;;){ int rc; rc = GetExitCodeThread(hThread, &exitCode); if(!rc && exitCode != STILL_ACTIVE) break;}→ → → → → →WaitForSingleObject(hThread, INFINITE);
Other benefits are:
- Busy loop wastes too much CPU time
- You can set the wait time
Wait for the end of multiple threads
The WaitForSingleObject function is not good at the same time to determine the state of multiple threads, WaitForMultipleObjects can wait for multiple threads at the same time, you can set whether to wait for all threads to finish or as soon as one thread finishes executing immediately.
Waiting in the GUI thread
Always go back to the main message loop in a GUI thread, and the two wait API functions block the main message loop. The MsgWaitForMultipleObjects function can be returned when the object is fired or when the message arrives.
Thread synchronization
Thread synchronization mainly has critical Sections, Mutex, semaphores, Event, except that the critical section is present in the process 内存空间
, others are 核心对象
.
Critical Sections
The Critical section is used to achieve exclusive possession between the individual threads of a single process when applied to the scope.
Examples of Use:
; // here must be global attributes to related threadInitializeCriticalSection (&cs );EnterCriticalSection(&cs );LeaveCriticalSection(&cs );DeleteCriticalSection(&cs );
Critical Sections Precautions:
- Once the thread enters a critical section and then calls the LeaveCriticalSection function, it can be repeatedly entered into the critical section.
- Never call sleep () or any wait in a critical section ... API functions.
- If the thread that enters the critical section ends or is dropped, and the LeaveCriticalSection function is not called, the system has no way to clear the critical section.
Advantages of the Critical section:
- It is very fast relative to the mutex. It takes almost 100 times times more time to lock an critical mutex than to lock a non-owned section. (critical section does not need to enter the operating system core)
Defects in the Critical section:
- Critical section is not a core object, cannot be WaitForSingleObject, there is no way to solve
死锁
the problem (a famous deadlock problem: The problem of philosophers eating)
- Critical section not available
跨进程
- Cannot specify the length of time to wait to end
- Not be able to have a critical section at the same time is waiting
- Unable to detect whether a thread has been discarded
Mutex
Mutexes can implement exclusive comrades among different threads, even if those threads belong to different processes.
Examples of Use:
// global attributeshMutex = CreateMutex ( NULL, // default event attributes false, // default not initially owned NULL // unnamed );DWORD dwWaitResult = WaitForSingleObject (hMutex , INFINITE );if (dwWaitResult == WAIT_OBJECT_0 ){ // wait succeed, do what you want ...}ReleaseMutex(hMutex );
Example Explanation:
1, Hmutex at the time of Creation 未被拥有
and 未激发
state;
2. Call Wait ... () function, the thread obtains Hmutex ownership, Hmutex momentarily becomes the excitation state, then wait ... The () function returns, at this time the state of the Hmutex is 被拥有
and 未激发
;
3, after ReleaseMutex, the state of Hmutex becomes 未被拥有
and 未激发
state
Mutex considerations:
- The ownership of the mutex does not belong to which thread that produced it, but the last wait for this mutex ... () operation and a thread that has not yet been ReleaseMutex () operation.
- If the thread owns a mutex and does not call ReleaseMutex () before the end, the mutex will not be destroyed, instead the mutex will be considered "not owned" and "not fired" and the next waiting thread will be notified by WAIT_ABANDONED_0.
- Wait ... The () function is returned when the mutex is in
未被拥有
and 未被激发
state.
- Set the second parameter of CreateMutex to TRUE to prevent race condition, otherwise the thread calling CreateMutex does not have a mutex and a context switch is owned by another thread.
Advantages of mutexes
- Core object, you can call wait ... () API function
- Cross-threading, cross-process, cross-user (add "global//" to CreateMutex's third parameter)
- Can be named, can be opened by other processes
- Can only be freed by which thread owns it
Mutex disadvantage
Semaphores
Semaphore is used to track limited resources.
Comparison with the mutex
- A mutex is a degradation of semaphore, and the maximum value of Semahpore is 1, which is a mutex
- Semaphore does not have the concept of ownership, there is no
wait_abandoned
state, a thread can repeatedly call wait ... () function to generate a lock, and the thread that owns the mutex no matter how many times it is called wait ... () function is not blocked.
- There are semaphore concepts in many systems, and mutexes are not necessarily.
- The thread that calls ReleaseSemaphore () is not necessarily called wait ... () of that thread, any thread can call ReleaseSemaphore at any time to unlock semaphore that is locked by any thread.
Semaphore advantages
- Core objects
- Can be named, can be opened by other processes
- Can be freed by any one thread
Semaphore Disadvantages
Event
The event is typically used for overlapped I/O, or for designing some custom synchronization objects.
Examples of Use:
handle hevent; //Global attributeshevent = CreateEvent (NULL, //default event attributes true, //mannual R ESET false, //nonsignaled NULL Span class= "hljs-comment" >//unnamed); SetEvent (hevent); PulseEvent (hevent);D Word dwwaitresult = WaitForSingleObject (hevent, INFINITE); ResetEvent (hevent); if (Dwwaitresult = wait_object_0) {//WAIT succeed, do-what-you w Ant... ResetEvent (hevent);
Example Explanation:
1, createevent default is non-excitation state, manual reset
2, SetEvent set the hevent as the excitation state
3, in the case of manual Reset (bmanualreset=true), PulseEvent set the event object to the excitation state, but wake the 所有
waiting thread, and then revert to the non-excitation state;
4, in the case of automatic Reset (bmanualreset=false), PulseEvent set the event object to the excitation state, but wake the 一个
waiting thread, and then revert to the non-excited state;
5. ResetEvent the hevent to the non-excited state
Event Considerations:
- The second parameter of the CreateEvent function bManualReset automatically resets to the non-excited state if the false,event becomes a firing state (thus waking up a thread);
- The second parameter of the CreateEvent function bManualReset if the true,event will not automatically reset to a non-excited state after it becomes a firing state (and thus a thread is awakened), it must be manually resetevent;
Event Benefits:
- Core objects
- Its state is completely controlled by the program and its status is not due to wait ... () function to change the call.
- Suitable for designing new synchronization objects
- Can be named, can be opened by other processes
Event Disadvantage:
- Requests to wake up are not stored and may be lost. If a Autoreset event object calls SetEvent or PulseEvent, and there are no threads waiting, the event will be lost. such as wait ... () function has not been called before the context Switch has occurred, this time setevent, this request for awakening will be lost, and then call wait ... () The function thread is stuck dead.
Alternative multithreading Overlapped I/O
Among the Win32 three basic I/O functions: CreateFile (), ReadFile (), and WriteFile ().
- Set the dwFlagsAndAttributes parameter of the CreateFile () function to file_flag_overlapped, then each action on the file will be OVERLAPPED. At this point, you can read and write to many parts of the file, there is no current concept of file location, each read and write to include its file location.
- If many overlapped requests are made, the order of execution is not guaranteed.
- Overlapped I/O cannot use the stdio.h function in the C Runtime library and can only perform I/O using ReadFile () and WriteFile ().
The Overlapped I/O function uses the Overlapped structure to identify each Overlapped operation that is currently in progress, while providing a shared area between the program and the operating system where parameters can be passed in both directions.
Multi-process
If a process dies, other processes in the system can continue to execute. The robustness of multi-process programs is much better than multithreading. Because if multiple threads are running in the same process, a misguided thread could ruin the entire process.
Another reason for using multiple processes is that when a program is ported from one job platform to another, such as UNIX (which does not support threading, but the cost of producing and ending the process is not expensive), UNIX applications tend to use multiple processes, and if porting becomes multithreaded, it may require a large change.
Literature
- Win32 multithread Study A-thread KeyWord
- Win32 multithread Study B-thread Usage
- Win32 multithread Study c-wait
- Win32 multithread Study d-synchronization
- Win32 multithread Study E-handle Thread
- Win32 multithread Study E-handle Process
Welcome to my personal blog click Me
Blog Original address: Win32 multithread Study summary-let ' s Thread
Subsequent blog content Maintenance will be updated at that address.
Win32 Multithreaded Programming Reading notes