Multi-threaded programming under the Windows platform

zhaozj2021-02-12  142

Multi-threaded programming under the Windows platform

Thread is a execution path of the process, which contains a stand-alone stack and CPU register status, each thread sharing all process resources, including open files, signal identity, and dynamically allocated memory. All threads in a process use the same address space, and the execution of these threads is controlled by the system scheduling program, and the scheduler determines which thread executable and when to perform threads. Threads have priority, priority threads must wait until the higher priority thread is executed after execution. On the machine's machine, the scheduler can put multiple threads on different processors, which makes the processor task balanced and improves the operating efficiency of the system.

Windows is a multi-task operating system that contains one or more threads in a process of Windows. The WIN32 API in the 32-bit Windows environment provides the interface functions required for multi-threaded application development, and the standard C library provided in the VC can also develop multi-threaded applications, and the corresponding MFC class libraries encapsulate multithreaded programming classes. The user can select the corresponding tool according to the needs and characteristics of the application during development. In order to fully understand Windows multi-threaded programming technology, this article will focus on how WIN32 API and MFC how to prepare multithreaded programs.

Multi-threaded programming is consistent with the principles of Win32 mode and the MFC class library support, and the main thread of the process can create new threads when needed. When the thread is executed, the thread is automatically terminated; all threads are terminated when the process is over. All active thread sharing processes, therefore, you need to consider a problem that conflicts when multiple threads accesses the same resource when programming. When a thread is accessing a process object, while another thread wants to change the object, it may generate an error result, and to resolve this conflict when programming.

Multi-threaded programming under Win32 API

Win32 API is an interface between Windows operating system kernel and application that performs function packaging of the function provided by the kernel, and the application obtains the corresponding system function by calling the associated function. In order to provide multi-threaded functions to the application, Win32 API function sets some function sets for processing multithreaded programs. Direct printing directly with Win32 API has many advantages: Win32-based application execution code is small, high running efficiency, but it requires more code written by programmers, and needs to manage all systems to provide resources. Direct written programs with Win32 API require programmers to have a certain understanding of the Windows system, which will occupy the programmer for many times to manage system resources, so the programmer's work efficiency is reduced.

1. Create and terminate threads with Win32 functions

The Win32 function library provides a function of multithreading, including creation threads, terminating threads, and establishing a mutually exclusive area. The function of creating a new thread in the application's main thread or other active thread is as follows:

Handle CreateThread (LPSecurity_Attributes LPthreadAttributes, DWORD DWSTACKSIZE, LPTHREAD_START_ROUTINE LPSTARTDRESS, LPVOID LPPARETER, DWORD DWCREATIONFLS, LPDWORD LPTHREADID);

If you have successful, return the handle of the thread, otherwise return null. After creating a new thread, the thread will start execution. However, if you use the create_suspended feature in dwcreationFlags, the thread does not execute it immediately, but will hang first, wait until you call ResumeThread to start the thread, you can call the following functions to set the thread priority:

Bool SetthreadPriority (Handle Hthread, Int nPriority);

When the function of the calling thread is returned, the thread is automatically terminated. If you need to terminate during the execution of the thread: Void ExitThread (DWORD DWEXITCODE);

If the thread is terminated outside the thread, the following functions can be called:

Bool TerminateThread (Handle Hthread, DWORD DWEXITCODE);

However, attention should be noted: This function may cause system unstable, and the resources occupied by the thread are not released. Therefore, in general, it is recommended not to use this function.

If the thread to be terminated is the last thread within the process, the corresponding process should be terminated after the thread is terminated.

2. Synchronization of thread

In the thread, if the thread is completely independent, the conflict with other resource operations such as the thread has no data access, it can be programmed according to the normal single-thread method. However, the situation is often not like this in multi-threading, and some resources are often accessed at the same time. Since conflict caused by shared resources is inevitable, in order to solve this thread synchronization problem, Win32 API provides a variety of synchronous control objects to help programmers resolve shared resource access conflicts. Let's introduce a wait function before introducing these synchronization objects because all control objects are used to use this function.

Win32 API provides a waiting function that enables thread to block its own execution. These functions generate signals in one or more synchronization objects in its parameters, or more than a predetermined wait time will return. When the wait function is not returned, the thread is waiting state, at which time the thread consumes very little CPU time. Using a waiting function can guarantee both synchronization of threads and improve the running efficiency of the program. The most commonly used waiting function is:

DWORD WAITFORSINGLEOBJECT (Handle Hhandle, DWORD DWMILLISECONDS);

The function WaitFormultiPleObject can be used to monitor multiple synchronization objects, which is:

DWORD WAITFORMULTIPLEOBJECT (DWORD NCOUNT, Const Handle * LPhandles, Bool Bwaitall, DWORD DWMILLISECONDS);

(1) Mutex object

The status of the Mutex object is only available when it is not owned by any thread, and there is no signal when it is owned. Mutex objects are ideal for coordinating multiple threads to mutual exclusive access to shared resources. You can use this object as follows:

First, establish a mutective object to obtain a handle:

Handle Createmutex ();

Then, before the thread may cause a conflict (ie, before accessing the shared resource), call WaitForsingleObject, pass the handle to the function, requested to occupy the mutually exclusive object:

DWWAITRESULT = WAITFORSINGLEOBJECT (HMutex, 5000L);

The shared resource access ends, releases the occupancy of the mutex object:

ReleaseMutex (HMUTEX);

The mutex object can only be occupied by one thread at the same time. When the mutex object is occupied by a thread, if another thread wants to occupy it, you must wait until the previous thread is released before successful.

(2) Signal object

Signal objects allow simultaneous access to multiple thread sharing resources, specifying the number of threads that can be accessed at the same time when creating objects. When a thread application is successful, the counter in the signal object is reduced, and after the ReleaseSemaphore function is called, the counter in the signal object plus one. Where the counter value is greater than or equal to 0, but less than or equal to the maximum value specified at the time of creation. If an application is creating a signal object, the initial value of the counter is set to 0, and other threads are blocked, and the resources are protected. After the initialization is completed, call the ReleaseSemaphore function to increase its counter to the maximum, and normal access is available. You can use this object as follows: First, create a signal object:

Handle createSemaphore ();

Or open a signal object:

Handle OpenSemaphore ();

Then, call WAITFORSINGLEOBJECT before the thread access shared resource.

After the shared resource access is completed, it should be released to the signal object:

ReleaseSemaphore ();

(3) Event object

Event Object (Event) is the simplest synchronization object, which includes both signal and no signal. Before the thread accesses a certain resource, you need to wait for an event to occur, and the event object is most appropriate. For example, the monitoring thread is activated only after the data is received in the communication port buffer.

The event object is built with the CreateEvent function. This function can specify the initial state of the class and events of the event object. If it is a manual reset event, it always maintains a signal state until it resets an eventless event with the resetEvent function. If it is an automatic reset event, then its status will automatically become no signal after a single wait thread is released. Use SetEvent to set the event object to a signal state. When establishing an event, you can name the object, so that threads in other processes can open the event object handle of the specified name with the OpenEvent function.

(4) Exclusion area object

When performing asynchronous execution in the exclusion zone, it can only share resource processing between the same process thread. Although several methods described above can be used, however, the method of using the rejection area is higher than the efficiency of synchronous management.

Define a critical area object for a critical_section structure first, call the object to initialize the object before the process uses:

Void InitializeCriticalSection (lpcritical_section);

When a thread uses the rejection area, call the function: EntercriticalSection or TryEntercriticalSection;

When the occupation is required, when exiting the rejection area, call the function LeaveCriticalSection, release the occupation of the exclusive object, for other threads.

MFC-based multi-threaded programming

MFC is a Microsoft's VC development integrated environment that provides the programmer's basic function library, which encapsulates the Win32 API to developers by way of class libraries. Due to its fast, simple, powerful, etc., is very popular among the developers. Therefore, it is recommended to use the MFC class library to develop applications.

In the MFC class library included in the VC , support for multi-thread programming, the basic principle is consistent with the WIN32 API design, but because the MFC is packaged, it is more convenient to implement, which is more convenient to avoid object handle management. Cool work.

In MFC, thread is divided into two types: working threads and user interface threads. The working thread is consistent with the thread described above, and the user interface thread is a thread that can receive the user's input, processing events, and messages.

Work thread

The work thread is more simple, the design idea is the basic consistency of the previous: a basic function represents a thread, creates and starts the thread, threads enter the running state; if the thread is used to share resources, resource synchronization is required. Create a thread in this way and can call a function when starting the thread: CWinThread * AfxBeginThread (AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL); Parameters pfnThreadProc is thread of execution Function, functional prototype: uint threadfunction (lpvoid pParam).

Parameters PPARAM is the parameter passed to the execution function;

Parameter nPriority is a thread execution permission, optional value:

Thread_priority_normal, thread_priority_lowest, thread_priority_highest, thread_priority_idle.

The parameter dwcreateflags is the flag at the time of thread, which can be used to create_suspended, indicating that the thread is created, and the RESUMETHREAD function is called, or the value "0" indicates that the thread is running after the thread is created.

The return value is the CWINTHREAD class object pointer, and its member variable m_hthread is a thread handle, and the function parameters of the thread operation are required in the Win32 API mode. So all Win32 API functions can be used when thread is created. PWINTHREAD-> M_Thread thread performs related operations.

Note: If you create and start a thread in a class object, the thread function should define a global function outside the class.

2. User interface thread

MFC-based applications have an application object, which is an object of CWINAPP derived class, which represents the main thread of the application process. When the thread is executed and exit the thread, the process is automatically ended due to the existence of other threads in the process. Class CWINAPP is derived from CWINTHREAD, and CWINTHREAD is the basic class of user interface threads. When writing a user interface thread, we need to derive our own thread class from CwinThread, and ClassWizard can help us complete this work.

First, a new class is derived with ClassWizard and sets the base class as CWINTHREAD. Note: The class DecLream_DyncReate and Implement_DyncReate macros are required because the objects that need to be dynamically created when creating a thread. The initialization and end code can be placed in the INITINSTANCE and EXITINSTANCE functions as needed. If you need to create a window, you can do it in the initInstance function. Then create a thread and start the thread. Two methods can be used to create a user interface thread, and the MFC provides two version of the AFXBEGINTHREAD function, one for creating a user interface thread. The second method is divided into two steps: First, call the constructor of the thread class Create a thread object; secondly, call the CWINTHREAD: CREATTHREAD function to create the thread. After the thread is established and started, it has been valid during the execution of the thread function. If it is a thread object, the thread is first ended before the object is deleted. CWINTHREAD has completed the work of the thread ended.

3. Thread synchronization

As we introduced several objects provided by the Win32 API, we have a class package in the MFC class library, and they have a common base class CSyncObject, and their correspondence is: Semaphore corresponds to CSemaphore, Mutex corresponds to CMUTEX, Event corresponds to CEVENT, CRITICALSECTION corresponding to CcriticalSection. In addition, MFC has also been encapsulated by two waiting functions, namely CSILOCK and CMULTILOCK. Due to four object usage, here is CMUTEX as an example: Create a cmutex object:

Cmutex Mutex (False, Null, NULL);

Or cmutex mutex;

Use the following code when each thread is to access the shared resource:

CSILOCK SL (& MUTEX);

Sl.lock ();

IF (sl.islocked ())

/ / Operate the shared resource ...

Sl.unlock ();

Conclude

If the user's application requires multiple tasks to perform the appropriate processing, use multithreading is ideal. Here, remind everyone that it is necessary to take special care of resource sharing and multi-thread debugging issues when multi-threaded programming.

转载请注明原文地址:https://www.9cbs.com/read-7266.html

New Post(0)