Preliminary Design of multi-thread and multi-task in Windows

Source: Internet
Author: User
In the current popular Windows operating system, it can run several programs simultaneously (independent programs are also called processes). For the same program, it can also be divided into several independent execution streams, which are called threads. threads provide multi-task processing capabilities. Studying software from the perspective of processes and threads is a common method nowadays. The emergence of the concepts of processes and threads is of great significance to improve the concurrency of software. Nowadays, all applications are multi-thread and multi-task processing, and the single-line city software is unimaginable. Therefore, it is essential for every programmer to master the multi-thread and multi-task design method. This article discusses the problems frequently encountered by multithreading technology in applications, such as inter-thread communication and synchronization.

1. Understand threads

To explain the thread, you have to mention the process. A process is an execution instance of an application. Each process is composed of a private virtual address space, code, data, and other system resources. The resources created by the process at runtime die with the termination of the process. The basic idea of a thread is very simple. It is an independent execution flow, an independent execution unit inside a process, which is equivalent to a sub-program, it corresponds to the cwinthread class objects in Visual C ++. When a separate execution program is running, the default operation contains a main thread. The main thread is in the form of a function address, such as the main or winmain function. It provides the startup point of the program. When the main thread ends, the process is terminated, but as needed, the application can be broken down into many independent threads, and each thread runs in the same process in parallel.

All threads in a process use the global variables and system resources of the process in the virtual address space of the process. The operating system allocates different CPU time slices to each thread. At a certain time point, the CPU executes only the threads in one time slice, and the corresponding threads in multiple time slices take turns in the CPU, since each time slice takes a short time, it seems that each thread is processed in parallel in the computer. The operating system schedules the CPU time based on the thread priority. The threads with higher priority run preferentially, And the threads with lower priority continue to wait.

There are two types of threads: User Interface thread and working thread (also known as background thread ). User Interface threads are usually used to process user input and respond to various events and messages. In fact, the main execution thread of an application, cwinapp, is a user interface thread, when an application is started, it is automatically created and started. The termination of the application also means that the application is terminated and the application is terminated in the city. A worker thread is used to execute background processing tasks of a program, such as computing, scheduling, and read/write operations on the serial port. The difference between a worker thread and a user interface thread is that it does not need to be created from the cwinthread class, the most important thing for it is how to implement the Running Control Function of a job thread task. The worker thread and the user interface thread must call different versions of the same function at startup. The reader must understand that all threads in a process share the variables of their parent process, at the same time, each thread can have its own variables.
Ii. thread management and operations

1. Thread startup

To create a user interface thread, you must first generate a derived class from the class cwinthread, and use declare_dyncreate and implement_dyncreate to declare and implement the cwinthread derived class.

The second step is to reload some member functions of the derived class as needed, such as: exitinstance (), initinstance (), onidle (), pretranslatemessage (), and then start the user interface thread, a version that calls the afxbeginthread () function: cwinthread * afxbeginthread (cruntimeclass * pthreadclass, int npriority = Priority, uint nstacksize = 0, DWORD dwcreateflags = 0, define lpsecurityattrs = NULL ); the first parameter indicates the defined user interface Thread class pointer variable, the second parameter indicates the thread priority, and the third parameter indicates the stack size corresponding to the thread, the fourth parameter is the additional flag when the thread is created. The default value is normal, for example, CREA. After the thread is started, the thread is suspended.

To start a worker thread, you must first compile a function that you want to run in parallel with the rest of the application, such as fun1 (), then define a pointer variable * pthread pointing to the cwinthread object and call the afxbeginthread (fun1, Param, priority) function. The return value is paid to the pthread variable and the thread is started to execute the above fun1 () function. Among them, fun1 is the name of the function to be run by the thread, which is also the name of the control function mentioned above. Param is an arbitrary 32-bit value that is to be transmitted to the thread function fun1, priority defines the priority level of the thread. It is a predefined constant. For more information, see msdn.

2. thread priority

The following cwinthread class member functions are used for thread priority operations:

Int getthreadpriority ();
Bool setthradpriority () (INT npriority );

The preceding two functions are used to obtain and set the priority of a thread. The priority here is a thread at the same priority level relative to the priority level of the thread, A thread with a higher priority runs first; a thread with different priority levels has a higher priority level and a thread with a higher priority level runs first. As for the constants required for priority setting, you can refer to msdn. Note that to set the thread priority, the thread must have the thread_set_information access permission during creation. The cwinthread class does not provide corresponding functions for setting the priority level of the thread, but can be implemented through the Win32 SDK functions getpriorityclass () and setpriorityclass.

3. Suspension and restoration of threads

The cwinthread class contains the functions used by the application to suspend and restore the threads it creates. suspendthread () is used to suspend the thread and pause the thread execution. resumethread () is used to resume the thread execution. If you execute suspendthread () several times in a row for a thread, You need to execute the resumethread () continuously to resume the thread running.

4. End thread

There are three ways to terminate a thread. A thread can call afxendthread () internally to terminate its own operation. It can call bool terminatethread (handle hthread, DWORD dwexitcode) outside the thread) to forcibly terminate the running of a thread, and then call the closehandle () function to release the stack occupied by the thread. The third method is to change the global variable to make the thread's execution function return, the thread is terminated. The third method is used as an example to show some code:

//////////////////////////////////////// ////////////////////////
///// Ctestview message handlers
//// Set to true to end thread
Bool bend = false; // defines the global variable used to control the running of the thread.
// The thread function
Uint threadfunction (lpvoid pparam) // thread function
{
While (! Bend)
{BEEP (1, 100,100 );
Sleep (1000 );
}
Return 0;
}
Cwinthread * pthread;
Hwnd;
//////////////////////////////////////// /////////////////////
Void ctestview: oninitialupdate ()
{
Hwnd = getsafehwnd ();
Pthread = afxbeginthread (thradfunction, hwnd); // start the thread
Pthread-> m_bautodelete = false; // The thread is manually deleted.
Cview: oninitialupdate ();
}
//////////////////////////////////////// ////////////////////////
Void ctestview: ondestroy ()
{Bend = true; // change the variable and end the thread.
Waitforsingleobject (pthread-> m_hthread, infinite); // wait until the thread ends
Delete pthread; // delete a thread
Cview: ondestroy ();
}
3. Communication between threads

Generally, a subthread needs to complete certain types of tasks for the primary thread, which implies that a communication channel needs to be established between the main thread and the secondary thread. Generally, there are several methods to implement this communication task: using global variables (this method is actually used in the previous example), using event objects, and using messages. Here we will mainly introduce the last two methods.

1. Use user-defined message communication

In Windows programming, every thread of an application has its own message queue, and even the working thread is no exception, this makes it very easy for threads to transmit information using messages. First, you need to define a user message, as shown below: # define wm_usermsg wmuser + 100; when necessary, call

: Postmessage (hwnd) Param, wm_usermsg, 0, 0)
Or
Cwinthread: postthradmessage ()

To send the message to another thread. The four parameters of the above function are the handle of the destination window to which the message will be sent, the message identifier to be sent, the message parameters wparam and lparam. The following code is the modification of the previous code. The modified result is that a dialog box is displayed at the end of the thread, prompting the thread to end:

Uint threadfunction (lpvoid pparam)
{
While (! Bend)
{
Beep (100,100 );
Sleep (1000 );
}
: Postmessage (hwnd, wm_usermsg, 0, 0 );
Return 0;
}
/////// The response function of the wm_usermsg message is onthreadended (wparam, lparam)
Long ctestview: onthreadended (wparam, lparam)
{
Afxmessagebox ("thread ended .");
Retrun 0;
}

The preceding example shows that the worker thread sends messages to the user interface thread. For the worker thread, if its design mode is also message-driven, then, the caller can send initialization, exit, and execute a specific processing message to it, so that it can be completed in the background. In the control function, you can directly use the SDK function: getmessage () to perform message inspection and processing, and implement a message loop by yourself. When the getmessage () function determines that the thread's message queue is empty, the thread gives the time slice allocated to the thread to other threads, which is not invalid and takes up the CPU time, if the message queue is not empty, obtain the message, determine the content of the message, and process it accordingly.

2. implement communication with event objects

It is complicated to transmit signals between threads for communication by using event objects, which are represented by the cevent class objects of MFC. The event object is in either of the two states: there is a signal and no signal, the thread can monitor the event in a signal State, so that the event operations can be performed in appropriate cases. The code in the preceding example is modified as follows:

//////////////////////////////////////// ////////////////////////////
Cevent threadstart, threadend;
//////////////////////////////////////// ////////////////////////////
Uint threadfunction (lpvoid pparam)
{
: Waitforsingleobject (threadstart. m_hobject, infinite );
Afxmessagebox ("thread start .");
While (! Bend)
{
Beep (100,100 );
Sleep (1000 );
Int result =: waitforsingleobject (threadend. m_hobject, 0 );
// Wait for the threadend event to have a signal. When there is no signal, the thread will hover over here.
If (result = wait_object_0)
Bend = true;
}
: Postmessage (hwnd, wm_usermsg, 0, 0 );
Return 0;
}
//////////////////////////////////////// /////////////////////
Void ctestview: oninitialupdate ()
{
Hwnd = getsafehwnd ();
Threadstart. setevent (); // The threadstart event has a signal.
Pthread = afxbeginthread (threadfunction, hwnd); // start the thread
Pthread-> m_bautodelete = false;
Cview: oninitialupdate ();
}
//////////////////////////////////////// ////////////////////////
Void ctestview: ondestroy ()
{Threadend. setevent ();
Waitforsingleobject (pthread-> m_hthread, infinite );
Delete pthread;
Cview: ondestroy ();
}

When the program is closed, a prompt box is displayed, showing "thread ended"
4. synchronization between threads

As we have mentioned earlier, each thread can access the public variables in the process. Therefore, when using multithreading, we must note how to prevent two or more threads from simultaneously accessing the same data, to avoid damage to data integrity. To ensure that each thread can be properly coordinated together is called synchronization between threads. The event object described in the previous section is actually a synchronization form. In Visual C ++, the synchronization class is used to solve the problem of data security caused by the parallel operation of the operating system. The seven multi-thread synchronization classes supported by MFC can be divided into two categories: synchronization objects (csyncobject, csemaphore, cmutex, ccriticalsection, and cevent) and synchronized access objects (cmultilock and csinglelock ). This section describes the critical section, mutexe, and semaphore. These synchronization objects enable various threads to work together and ensure program running is safer.

1. Critical Section

The critical section ensures that only one thread can access data at a certain time. When using it, you need to provide each thread with a shared critical area object. No matter which thread occupies the critical area object, you can access protected data. At this time, other threads need to wait, until the thread releases the object in the critical section, after the critical section is released, other threads can occupy this critical section to access Shared data. The critical section corresponds to a ccriticalsection object. When a thread needs to access data protection, it calls the lock () member function of the critical section object. After the data protection operation is completed, call the unlock () member function of the object in the critical section to release the ownership of the object in the critical section, so that another thread can capture the object in the critical section and access protected data. Start two threads at the same time. Their corresponding functions are writethread () and readthread (), which are used to operate on the array [] of the Public Array Group. The following code illustrates how to use the critical area object:

# Include "afxmt. H"
Int array [10], destarray [10];
Ccriticalsection section;
//////////////////////////////////////// ////////////////////////////////
Uint writethread (lpvoid PARAM)
{Section. Lock ();
For (INT x = 0; x <10; X ++)
Array [x] = X;
Section. Unlock ();
}
Uint readthread (lpvoid PARAM)
{
Section. Lock ();
For (INT x = 0; x <10; X ++)
Destarray [x] = array [x];
Section. Unlock ();
}

The result of running the code above should be that the elements in the destarray array are respectively 1-9, rather than disordered numbers. If synchronization is not used, this is not the result. Interested readers can experiment with it.
2. Mutual Exclusion

Mutex is similar to the critical section, but it is relatively complicated to use. It not only synchronizes between threads of the same application, but also between different processes, in this way, resources can be securely shared. The mutex corresponds to a cmutex object. When using a mutex object, you must create a csinglelock or cmultilock object for actual access control, because the example here only processes a single mutex, therefore, we can use the csinglelock object. The lock () function of this object is used to possess mutex, and unlock () is used to release mutex. The implementation code is as follows:

# Include "afxmt. H"
Int array [10], destarray [10];
Cmutex section;

//////////////////////////////////////// /////////////////////
Uint writethread (lpvoid PARAM)
{Csinglelock singlelock;
Singlelock (& Section );
Singlelock. Lock ();
For (INT x = 0; x <10; X ++)
Array [x] = X;
Singlelock. Unlock ();
}
Uint readthread (lpvoid PARAM)
{Csinglelock singlelock;
Singlelock (& Section );
Singlelock. Lock ();

For (INT x = 0; x <10; X ++)
Destarray [x] = array [x];
Singlelock. Unlock ();

}

3. semaphores

The semaphore usage is similar to mutex usage. The difference is that it allows multiple threads to access the same resource at the same time. To create a semaphore, you must declare an object using the csemaphore class, once a semaphore object is created, it can be used to access resources. To implement counting, first create a csinglelock or cmltilock object, and then use the lock () function of this object to reduce the counting value of this semaphore. Unlock () and vice versa. The following code starts three threads respectively. Two message boxes are displayed at the same time during execution, and the third message box is displayed 10 seconds later.

//////////////////////////////////////// /////////////////////////
Csemaphore * semaphore;
Semaphore = new csemaphore (2, 2 );
Hwnd = getsafehwnd ();
Afxbeginthread (threadproc1, hwnd );
Afxbeginthread (threadproc2, hwnd );
Afxbeginthread (threadproc3, hwnd );
//////////////////////////////////////// //////////////////////////////
Uint threadproc1 (lpvoid PARAM)
{Csinglelock singellock (semaphore );
Singlelock. Lock ();
Sleep (10000 );
: MessageBox (hwnd) Param, "thread1 had access", "thread1", mb_ OK );
Return 0;
}
Uint threadproc2 (lpvoid PARAM)
{Csinglelock singellock (semaphore );
Singlelock. Lock ();
Sleep (10000 );
: MessageBox (hwnd) Param, "thread2 had access", "thread2", mb_ OK );
Return 0;
}
Uint threadproc3 (lpvoid PARAM)
{Csinglelock singellock (semaphore );
Singlelock. Lock ();
Sleep (10000 );
: MessageBox (hwnd) Param, "thread3 had access", "thread3", mb_ OK );
Return 0;
}

For complex applications, thread applications provide efficient, fast, and secure data processing capabilities for applications. This article describes the problems frequently encountered in the thread and hopes to help readers.
 

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.