Multi-Thread Programming Problems
1. What is a process? What is a thread?
A process is a collection of system object ownership. For example, if a process has a memory context and a file handle, it can be derived from multiple threads or many DLL modules. In Windows, a process does not complete the actual work. It only provides a relatively independent runtime environment, and the thread is the carrier to complete the actual work. The thread belongs to the process and shares the system objects owned by the process. A thread is the scheduling unit of the operating system. Essentially, a thread is a piece of executable code.
Advantages and disadvantages of adopting multi-process:
Advantage: The running environment is relatively independent. The crash of a process generally does not affect the execution of other processes.
Disadvantages:
Time-consuming resources: to start a process, you need to apply for a large amount of system resources, including virtual memory, file handles, and loading various necessary dynamic link libraries. The thread does not need the above actions, because it shares all resources in the process.
"It may take several MB for the system to prepare a process environment"
Complex communication: the address space of a process is independent. process a's address X may be meaningless in process B. In this way, when data needs to be shared between processes, special mechanisms are required to complete these tasks. The thread is in the same address space, facilitating data sharing. "A thread is a cheap choice. It is easy to have 500 threads on a Windows server, but it is hard to imagine 500 processes ".
Ii. Why multithreading is required (explain when to consider using threads)
From the user's perspective, it is to get better system services; from the perspective of the program itself, it is to make the target task as quickly as possible to make more effective use of system resources. Generally, multithreading is required in the following scenarios:
1. When a program contains complex computing tasks
It mainly uses multithreading to obtain more CPU time (resources ).
2. peripheral devices with slow processing speed
For example, when printing. For another example, network programs involve packet sending and receiving. The time is variable. Using Independent threads to process these tasks does not require the program to wait for results.
3. Programming Needs
Windows is a preemptible multi-task System Based on Message loops. In order to prevent the message loop system from being congested, the program needs multiple threads to complete some tasks together.
Iii. Problems with multithreading (list problems)
In fact, simply using a thread will not cause any problems. It is very simple to Start, Run, and end. In the Win32 environment, start: createthread. Running is the process of executing the function. Stopping is the process returned by the function or calling exitthread. However, the following reasons may cause a series of problems in the process of using the thread:
1. Version Problems
The concept of multi-task is generated with the proposal of actual requirements. The program designer did not consider the need to run the code in a multi-threaded environment. Using the code in a multi-threaded environment will undoubtedly lead to access conflicts. The most typical example is the C Runtime Library. The earliest C Runtime Library was born in 1970s. At that time, multi-tasking was a novel concept, let alone multithreading, the Library of this version uses a large number of global variables and static variables (the root cause of the competition condition is not required for local variables, because local variables use stacks, each thread has its own stack space. In addition, when starting a thread, the parameter for the thread function should be used as much as possible, rather than a pointer or reference, this avoids conflicts. For example, if an errno variable is used in the library to indicate the program's error code, if the library is used in multiple threads, when you need to set error codes, a conflict occurs.
To prevent the preceding problems, VC provides another thread-safe C Runtime Library. Therefore, when writing a multi-threaded program, note whether the version of the connected database is correct (this process is generally completed by the Application Wizard, So programming is normal ). There are also some other versions related to this: Single-thread version, multi-thread version debugging version, and multi-thread release version.
2. Race Condition)
Generally, threads do not act independently. Generally, multiple threads are divided and collaborated to complete different small tasks in a large task. At this time, these threads need to operate some resources together, A typical example is when multiple threads perform file operations or screen printing: When thread a writes half of the file, context switch occurs, another thread B continues to write files, and the file content will be messy. It even causes exceptions and errors. A typical example is that for three threads, thread a applies for a piece of memory in the heap and fills in a value. Thread B reads the value and releases the memory, if thread C still needs to operate on the memory, it will cause an exception.
3. Inter-thread communication problems
When a thread completes a task in collaboration, it sometimes needs to communicate to control the task execution steps. A typical example is the reader thread: After the write thread finishes writing data to a memory area, the read thread must be notified to read the data. After reading the data, the write thread must be notified to continue writing data into the data. A wider example is that a thread needs to wait for an event to determine whether to continue working. At this time, if the thread execution process is not properly controlled, unexpected errors will occur.
4. Reduced system efficiency due to nonstandard threads
A process contains more than one thread. These threads may dynamically apply for certain resources. For example, some database threads may dynamically load dynamic database links, however, when the thread ends, the dynamic link library is forcibly terminated if it is not released in time. Therefore, the reference count of the dynamic link library in the process is not 0, as a result, the dynamic link library contains a copy in the process. When this happens frequently, the system efficiency will be greatly affected.
4. Thread type (explaining the differences and connections between the UI thread and worker thread)
Strictly speaking, there is no essential difference between a thread, but the difference between a UI thread and a worker thread is repeatedly emphasized in the Win32 programming document. And their definitions are given:
The UI thread is a thread with a message queue and window, and its main responsibility is to process window messages. The worker thread does not have a message queue, but when the worker thread generates a user interface (except the message box and mode dialog box), the thread changes and becomes the UI thread.
Problem:
1. Thread message queue and window message queue
In Win32, each thread has its own exclusive message queue, and the window does not always have a message queue, because a UI thread can create many windows.
2. What is the difference between the UI thread and the worker thread?
Different responsibilities: the UI thread is responsible for processing user interface-related messages. Generally, user interface messages come from user input (such as mouse and keyboard messages) and system messages (such as wm_paint) and user-defined messages generated by the program. Therefore, there is usually no waiting (wait…) in this thread ...) Function, so that the thread will be suspended, thus affecting the processing of message queues. The worker thread does not need to process user interface messages, but completes general computing tasks. When the thread waits for necessary resources in the computing process, the interface refreshing action is not affected.
The management of the operating system is different: For the UI thread, generating a UI thread actually produces two threads, one of which is its own, the other is the shadow thread generated by the operating system in response to its GDI call.
3. What is the problem of changing a worker thread to a UI thread?
A worker thread is generally used for computing. If it is converted to a UI thread, the message response on the user interface will not be taken into account.
4. Can a worker thread have its own message queue?
A worker thread can also have its own message queue. This queue is generally created by calling peekmessage () and parsed by calling getmessage. (See the source code for specific implementation)
5. Use the following rules to manage the interaction between threads, messages, and windows in Win32.
All messages sent to a window are processed by the thread that generates the window.
5. Start and Stop threads (explain the different methods of starting threads and their differences and practical scenarios)
The thread startup methods vary with the update of the C Runtime Library and the programming environment. The following describes several typical thread startup methods.
1. _ beginthread and _ endthread
This function is a function in the C Runtime Library. It initializes the function library. Its prototype is unsigned long _ beginthread (void (_ cdecl * start_address) (void *), unsigned stack_size, void * Arglist); "this function is considered to be a simple-minded function." using this function causes the creation thread to be unable to be effectively controlled. If this thread cannot be suspended at startup, priority cannot be set for this thread. In addition, this function is used to hide the Implementation Details of Win32. The first thing to start a thread is to close its own handle. Therefore, this handle cannot be used to wait for the end of the thread and other operations. This function is a product of the early C Runtime Library and is not recommended. The later improved version is _ beginthreadex.
Threads started through _ beginthread should end by calling _ endthread to clear thread-related resources.
2. _ beginthreadex and _ endthreadex
This function is a function in the C Runtime Library, which is implemented in Standard C. It is more powerful than _ beginthread and _ beginthreadex for thread control (three more parameters than the former ), is the enhanced version of _ beginthread. The prototype is unsigned long _ beginthreadex (void * Security, unsigned stack_size, unsigned (_ stdcall * start_address) (void *), void * Arglist, unsigned initflag, unsigned * thrdaddr ); this function returns the handle of the new thread, which can be used to control the thread. Although this function is written in standard C (it can be transplanted to other systems without modification ), however, because it is closely related to the Windows System (handle generated by the thread needs to be manually closed), it is often necessary to include windows. h.
The thread started through _ beginthreadex is cleared by calling _ endthreadex.
3. createthread and exitthread
Createthread is a function in the Win32 API function set. Its prototype is handle createthread (lpsecurity_attributes attributes, DWORD dwstacksize, lpthread_start_routine lpstartaddress, lpvoid lpparameter, DWORD wcreationflags, lpdword lpthreadid ); this function uses the type conventions in the Win32 programming environment and applies only to Windows systems. The parameter format is the same as _ beginthreadex, And the thread control capability is also the same, but this function has nothing to do with the C Runtime Library. It is not responsible for initializing the library, so in the multi-threaded environment, if you use this function to start a thread, you should not use the multi-threaded function in the C Runtime Library. Instead, it should be a Win32 API function with corresponding functions. In addition, you should manually provide the code for thread synchronization.
The thread created through createthread is cleared through exitthread.
4. afxbeginthread and afxendthread
Afxbeginthread is a thread startup method provided by MFC. It is a heavy-load function and has two calling forms: worker thread version and UI thread version. MFC carefully encapsulates Win32 threads (cwinthread). Although it always calls _ beginthreadex to start a thread, its additional work makes it possible in the MFC environment, the operation thread becomes simple and clear and does not require too much attention to details. MFC mainly implements the following tasks in thread encapsulation:
1. automatically clear the cwinthread object
2. Close the thread handle and release the thread object automatically.
3. Stores important thread-related parameters, namely, thread handle and thread ID.
4. It is supplemented by other MFC synchronization objects to facilitate thread synchronization.
5. Use strict asserted debugging statements to make thread debugging relatively simple
"(C Runtime Library is a set of practical functions developed with standard C) If a multithreaded program uses a standard C library function and uses createthread () and exitthread (), this will cause memory leakage. To solve this problem, use the run-time library function of C to start and terminate the thread, instead of createthread () and exitthread () defined by Win32 API (). In C Runtime library functions, their alternative functions are _ beginthreadex () and _ exitthreadex (), and the header file is _ process. h. In vc6.0, select multithreaded Runtime Library from project> Settings> C/C ++> code generation. Of course, the above problem can also be solved by avoiding the use of C standard library functions. Win32 provides some replacement functions for C standard library functions, such as wsprintf () and lstrlen () to replace sprintf () and strlen (). In this way, using createthread () and exitthread () will not cause problems ."
6. Thread Synchronization (introduces the synchronization mechanism in Windows)
1. How to wait for a thread to end (busy loop) and efficient wait (waitforsingleobject ))
1) busy wait (busy loop)
Hthrd = createthread (null, 0, threadfunc, (lpvoid) 1,0, & threadid );
For (;;)
{
Getexitcodethread (hthrd, & exitcode );
If (exitcode! = Still_active)
Break;
}
Closehandle (hthrd );
Disadvantage: it consumes CPU resources. If you wait in the UI thread, the window cannot be refreshed. It is not recommended.
2) Efficient waiting
(1) waitforsingleobject;
For the waitforsingleobject parameter, the former is the waiting object, and the latter is the waiting time. For some threads with long execution time, you can set a proper value. After waiting for this time, update the interface, and then wait, or forcibly terminate the thread.
Change the code for the above waiting part:
Waitforsingleobject (hthrd, infinite );
This function is equivalent to a sleep function. When the waiting object (handle) is not triggered, the waiting thread is automatically suspended. This method solves the CPU time-consuming problem. However, you cannot use this method in the UI thread to wait for the end of a thread.
Solution: Create a worker manager thread, wait in the thread, and the worker thread completes. Then, the manager thread sends a message to notify the UI thread to update the window.
(2) waitformultipleobject
This function allows you to wait for multiple objects at the same time. The prototype of the function is as follows:
DWORD waitformultipleobject (DWORD ncount, const Hande * lphandles, bool bwaitall, dwmilliseconds );
The first parameter indicates the size of the handle array; the waiting object cannot exceed 64
The second parameter is the handle array;
The third parameter indicates whether to wait for all objects to be fired. True indicates yes.
The fourth parameter is the waiting time.
About the return value of waitformultipleobject:
When bwaitall is true, the returned value is wait_object_0;
When bwaitall is false, the return value minus wait_object_0 is the subscript of the stimulated object.
Application:
A) solve the problem of multiple workers n completing multiple tasks M (n <m) (bwaitall is set to false)
The solution is as follows: first, N tasks are extracted from M tasks, corresponding to N workers, and then the function is used to wait for any worker to finish the task, once completed, let it do another task.
B) Solve the Problem of waiting for multiple resources (bwaitall is set to true)
Dining for philosophers: Five philosophers are at the Round Table. Each philosopher has one chopsticks on the left hand side. philosophers are doing two things: Eating and thinking. They need two chopsticks at the same time.
Solution: Simulate philosophers as threads and chopsticks as resources. Only when philosophers obtain two resources at the same time can they take further actions (EAT ). That is:
Waitformultipleobjects (2, mychopsticks, true, infinite );
Mychopsticks is an array of 5 core objects.
(3) msgwaitformultipleobjects
Prototype:
DWORD msgwaitformultipleobjects (DWORD ncount, const handle phandles, bool fwaitall, DWORD dwmilliseconds, DWORD dwwakemask );
The meanings of the preceding parameters are the same as those of waitformultipleobject. The last one is the message shield identifier, indicating the type of message to be received. The returned value also has an additional significance: when the message arrives, this function returns wait_object_0 + ncount. The following are common architectures using msgwaitformultipleobjects:
While (! Quit)
{// Wait for next message or object being signaled
DWORD dwwake;
Dwwake = msgwaitformultipleobjects (
Gnumprinting,
GPRS jobs,
False,
Infinite,
Qs_allevents );
If (dwwake> = wait_object_0 & dwwake <wait_object_0 + gnumprinting)
{
// The object is triggered.
} // End if
Else if (dwwake = wait_object_0 + gnumprinting)
{
// Message arrival
While (peekmessage (& MSG, null, 0, 0, pm_remove ))
{// Get next message in queue
If (msg. Message = wm_quit)
{
Quit = true;
Exitcode = msg. wparam;
Break;
} // End if
Translatemessage (& MSG );
Dispatchmessage (& MSG );
} // End while
}
} // End while
2. How to effectively control a thread
In any case, remember that the core attribute of a thread is the handle of the thread and the ID of the thread. Therefore, to control a thread, you also need to start from these two aspects.
1) Use the startup function that can return the thread handle to start the thread (except _ beginthread)
(2) Try not to make a thread with a large workload "stuffy gourd", so that the thread can receive external notification messages, such as the following code:
MSG;
While (1)
{
Peekmessage (& MSG, null, 0, 0, pm_remove );
If (msg. Message = wm_my)
Break;
Sleep (100 );
}
Note: getmessage is also a function used to obtain a message in a message queue. The difference is that getmessage is synchronized, that is, if there is no message in the message queue, the thread will automatically suspend. Using getmessage can make the worker thread a one-step thread!
MSG;
While (getmessage (& MSG, null, 0, 0 ))
{
If (msg. Message = wm_my)
{
// Do something here
}
}
The above process can also be implemented through the event object.
Unresolved question: how to control a thread waiting for other events. For example, if a TCP listening thread is listen on a socket, the thread is suspended! But now the main thread needs to shut down this thread again. What should I do!
3. How to mutually access a resource (cmutex and critical section)
When do I need a mutex?
Common scenarios: multiple threads need to operate the same linked list (the head pointer of the chain table) from time to time ); multiple Threads need to write files or screen output (lock file handle or screen handle) from time to time; multiple threads need to operate on a counter (lock this variable) from time to time ); in a multi-threaded environment, when accessing global variables, static variables, and heap memory, consider whether a race condition may occur ).
1) mutex
Win32 provides a complete set of mechanisms for access to mutex resources, one of which is the mutex. MFC encapsulates these API functions to form a cmutex mutex class, both methods can achieve mutually exclusive access to resources.
API in Win32:
Createmutex:
Prototype:
Handle createmutex (lpsecurity_attributes lpmutexattributes, bool binitialowner, lpctstr lpname );
The first parameter is a security attribute;
The second parameter indicates whether the mutex owner is the current thread;
The third parameter is the mutex name;
When the mutex is no longer needed, you should call closehandle to disable it.
Convention: After the mutex is generated, a thread locks the lock (that is, wait... Function). At this time, the system will hand over the mutex ownership to this thread, and then temporarily set this object to an exciting state, so wait... After the function is returned, call releasemutex to release the ownership after the corresponding work (such as modifying the linked list pointer, modifying the counter, and writing the file. Cycle.
Cmutex object in MFC:
A. Use its constructor to generate a mutex object
Handle createmutex (lpsecurity_attributes lpmutexattributes, bool binitialowner, lpctstr lpname );
B. Use csinglelock or cmutiplelock to generate a temporary object and lock and release the mutex;
2) critical section
Another mechanism that provides mutex access is the critical section, which is cheaper than the previous method because it is not a core object of the system. The critical section can be accessed repeatedly, which is different from mutex, when using the critical section, we need to ensure that the number of incoming requests equals the number of outgoing requests.
The related functions are initializecriticalsection, deletecriticalsection, entercriticalsection, and leavecriticalsection.
4. How to wait for multiple (or identical) resources (waitformultiobject)
Wait for multiple resources to be used in multi-threaded programming. For example, wait for a thread to end and a resource to be released, and wait for the buffer zone and the device to prepare two resources, you can set System Objects for different resources and then use waitformultiobject to wait.
5. How to wait for one of multiple resources (using csemaphore)
In reality, the following situations may also occur: the number of cameras rented by a guest is N, which is required by a number of guests. After the camera is rented, the guest must wait, if there is a camera, a guest can wait for rent. There are still many problems that can be summarized using this producer/consumer model.
This is the case where one of the multiple resources is waiting. in Win32 programming, semaphores (semaphore) are often used to solve this problem.
In Win32 systems, semaphores have the following features:
A semaphore can be locked for n times. N generally indicates the number of available resources. In the preceding example, it indicates the number of cameras. After the semaphore is initialized, wait is called once in the Win32 environment... An operation is a lock on the volume. The semaphore value is incremented by 1. After the operation is completed, the releasesemaphore operation is called, indicating that the resource is released (in the above example, the camera is returned ). MFC encapsulates the relevant API functions of Win32 semaphores (csemaphore) and works with cmultilock or csinglelock to implement lock and resource release.
7. Inter-thread Communication
There are many ways to implement inter-thread communication, and different applications may vary depending on the situation. There are roughly two types of communication: Intra-process thread communication and inter-process communication. The synchronization mutex wait mechanisms described earlier can also be attributed to inter-thread communication,
1. Use thread messages for thread Communication
2. Use event objects for thread Communication
Win32 also provides a flexible core object, which is completely controlled by the Program (Only recycled by the system when it is cleared). This is the event object. Event objects are generally used for notifications between threads. The following describes some attributes of the event object:
To create an event object, you can call the Win32 API function or use the event object encapsulated by MFC. Its API prototype is:
Handle createevent (lpsecurity_attributes lpeventattributes, bool bmanualreset, bool binitialstate, lptstr lpname );
The second parameter indicates whether the event object is manually modified (the resevent function needs to be explicitly called for manual modification). The third parameter sets the initial state of the event object. True indicates the excited state, and false indicates the non-excited state. The fourth parameter is the event name.
After the event object is created, it is flipped between the excited state and the non-excited state under the control of the program.
8. debugging of thread code
9. What is thread-safe code?
10. Principles of multi-threaded programming