VC + + Multithreaded programming-inter-thread communication and thread synchronization

Source: Internet
Author: User
Tags semaphore

Reference: http://blog.csdn.net/zjc0888/article/details/7372258

Inter-thread communication

In general, a secondary thread in an application always performs a specific task for the main thread, so that there must be a channel of information passing between the main thread and the secondary threads, that is, the main thread and the secondary threads are communicating. This communication between threads is not only unavoidable, but also complex and frequent in multithreaded programming, as described below.

Using global variables for communication

Because each thread that belongs to the same process shares the resources that the operating system allocates for that process, one of the simplest ways to resolve inter-threading communication is to use global variables. For a standard type of global variable, we recommend using the volatile modifier, which tells the compiler that there is no need to optimize the variable without putting it in a register, and that the value can be changed externally. If the information required to pass between threads is more complex, we can define a structure that passes information by passing pointers to the structure.
 
Using a custom message

We can send a custom message to another thread in the execution function of one thread to achieve the purpose of communication. One thread sends a message to another thread that is implemented by the operating system. Using the message-driven mechanism of the Windows operating system, when a thread issues a message, the operating system first receives the message, then forwards the message to the target thread, and the thread that receives the message must have already established a message loop.
Routine 7 MultiThread7

This routine demonstrates how to use custom messages for inter-thread communication. First, the main thread sends a message to the Ccalculatethread thread wm_calculate,ccalculatethread the thread receives the message, evaluates it, sends a WM_DISPLAY message to the main thread, and the main thread receives the message and displays the result of the calculation.

Create a dialog-based project MULTITHREAD7, add three radio button Idc_radio1,idc_radio2,idc_radio3 to the dialog idd_multithread7_dialog, respectively, with the title 1+2+3+4+ ... +10,1+2+3+4+......+50,1+2+3+4+......+100. Join the button idc_sum, titled "Sum". Add the Label box Idc_status, and select "Border" for the property;
The following variables are defined in MultiThread7Dlg.h:
Protected
int naddend;
Represents the size of the addend.

Double-click the three radio buttons to add a message response function:
void Cmultithread7dlg::onradio1 ()
{
naddend=10;
}

void Cmultithread7dlg::onradio2 ()
{
naddend=50;

}

void Cmultithread7dlg::onradio3 ()
{
naddend=100;

}
And in the OnInitDialog function to complete the corresponding initialization work:
BOOL Cmultithread7dlg::oninitdialog ()
{
......
((cbutton*) GetDlgItem (idc_radio1))->setcheck (TRUE);
naddend=10;
......
Add in MultiThread7Dlg.h:
#include "CalculateThread.h"
#define Wm_display wm_user+2
Class Cmultithread7dlg:public CDialog
{
Construction
Public
Cmultithread7dlg (cwnd* pparent = NULL); Standard constructor
ccalculatethread* M_pcalculatethread;
......
Protected
int naddend;
LRESULT Ondisplay (WPARAM wparam,lparam LPARAM);
......
Add in MultiThread7Dlg.cpp:
Begin_message_map (Cmultithread7dlg, CDialog)
......
On_message (Wm_display,ondisplay)
End_message_map ()

LRESULT Cmultithread7dlg::ondisplay (WPARAM wparam,lparam LPARAM)
{
int ntemp= (int) WParam;
Setdlgitemint (Idc_status,ntemp,false);

return 0;

}
The above code allows the main thread class Cmultithread7dlg to process the Wm_display message, which displays the result of the calculation in the Idc_status label box.
Double-click the button idc_sum to add the message response function:
void Cmultithread7dlg::onsum ()
{
M_pcalculatethread=
(ccalculatethread*) AfxBeginThread (Runtime_class (Ccalculatethread));

Sleep (500);

M_pcalculatethread->postthreadmessage (Wm_calculate,naddend,null);
}
The role of the Onsum () function is to establish a calculatethread thread that delays sending wm_calculate messages to the thread.
Right-click the project and select "New class ..." To add the base class for the project to CWinThread derived thread class Ccalculatethread.

Add in File CalculateThread.h
#define Wm_calculate wm_user+1
Class Ccalculatethread:public CWinThread
{
......
Protected
afx_msg LONG OnCalculate (UINT wparam,long lParam);
......
Add in File CalculateThread.cpp
LONG ccalculatethread::oncalculate (UINT wparam,long LParam)
{
int ntmpt=0;
for (int i=0;i<= (int) wparam;i++)
{
Ntmpt=ntmpt+i;
}

Sleep (500);
::P ostmessage (HWND) (Getmainwnd ()->getsafehwnd ()), wm_display,ntmpt,null);

return 0;
}
Begin_message_map (Ccalculatethread, CWinThread)
{{Afx_msg_map (ccalculatethread)
Note-the ClassWizard would add and remove mapping macros here.
}}afx_msg_map
On_thread_message (Wm_calculate,oncalculate)
Compared to the main thread, note the difference between them
End_message_map ()
Add an article at the beginning of the CalculateThread.cpp file:
#include "MultiThread7Dlg.h"
The above code for the Ccalculatethread class added Wm_calculate message, the response function of the message is OnCalculate, its function is based on the value of the parameter wParam, the cumulative result in the temporary variable ntmpt, Delay 0.5 seconds, send Wm_display message to the main thread to display, ntmpt as a parameter pass.
Compile and run the routine to realize how messages are passed between threads.

Synchronization of Threads

While multi-threading can benefit us, there are a number of issues that need to be addressed. For example, for an exclusive system resource such as a disk drive, because the thread can execute any code snippet of the process, and the thread's run is automatically completed by the system scheduler, there is a certain degree of uncertainty, so it is possible that two threads will operate on the disk drive at the same time, resulting in operational errors; For a computer on a banking system, it is possible to use one thread to update its user database, and another to read the database in response to the needs of depositors, and it is highly probable that the thread reading the database reads a database that is not fully updated because it is possible that only a subset of the data has been updated while reading.

Synchronizing the threads that belong to the same process is called thread synchronization. MFC provides a variety of synchronization objects, below we only introduce the most common four kinds:

Critical section (CCriticalSection)
Event (CEvent)
Mutex Amount (CMutex)
Signal Volume (CSemaphore)
 
With these classes, we can do thread synchronization more easily.

A. Using the CCriticalSection class

You can use the critical section object when multiple threads access an exclusive shared resource. At any one time only one thread can have a critical section object, a thread with a critical section can access the protected resource or code snippet, and other threads wishing to enter the critical section will be suspended until the critical section is discarded by the thread that owns the critical section, ensuring that multiple threads are not accessing the shared resource at the same time.

The CCriticalSection class is very simple to use, with the following steps:
 

Defines a global object for the CCriticalSection class (so that each thread can access it), such as CCriticalSection critical_section;
Before accessing the resource or code to be protected, call the member of the CCriticalSection Class lock () to obtain the critical section object:
critical_section. Lock ();
The function is called in the thread to get the critical section it requested. If no other thread occupies a critical section object at this time, the thread that calls lock () obtains a critical section, otherwise the thread is suspended and placed in a system queue until the thread that currently owns the critical section releases the critical section.
After the Access critical section is complete, use the member function of CCriticalSection unlock () to release the critical section:
critical_section. Unlock ();
In a more popular sense, thread a executes to critical_section. Lock (); statement, if other thread (B) is executing critical_section. Lock (); statement and critical_section. Unlock (); The statement before the statement, thread a waits until thread B finishes executing critical_section. Unlock (); statement, thread A will continue execution.
This is illustrated by an example.


Routine 8 MultiThread8

Create a dialog-based project MultiThread8, add two buttons and two edit box controls to the dialog Idd_multithread8_dialog, and two buttons with IDs Idc_writew and idc_writed respectively, with the title "Write ' W" "and" Write ' D ' "; Two the ID of the edit box is Idc_w and idc_d, respectively, the attributes are selected read-only;
Declare two thread functions in a MultiThread8Dlg.h file:
UINT Writew (LPVOID pparam);
UINT writed (LPVOID pparam);
Add CEdit class variables m_ctrlw and M_ctrld to Idc_w and idc_d, respectively, using ClassWizard;
In the MultiThread8Dlg.cpp file, add the following:

In order to correctly use the synchronization class in the file, add at the beginning of the file:
#include "Afxmt.h"
Defines a critical section and an array of characters, in order to be able to be used among different threads, defined as global variables:
CCriticalSection critical_section;
Char g_array[10];
To add a thread function:
UINT Writew (LPVOID pparam)
{
CEdit *pedit= (cedit*) Pparam;
Pedit->setwindowtext ("");
critical_section. Lock ();
Lock critical section, other threads encounter critical_section. Lock (); statement to wait
Until the critical_section is executed. Unlock (); statement
for (int i=0;i<10;i++)
{
g_array[i]= ' W ';
Pedit->setwindowtext (G_array);
Sleep (1000);
}
critical_section. Unlock ();
return 0;

}

UINT writed (LPVOID pparam)
{
CEdit *pedit= (cedit*) Pparam;
Pedit->setwindowtext ("");
critical_section. Lock ();
Lock critical section, other threads encounter critical_section. Lock (); statement to wait
Until the critical_section is executed. Unlock (); statement
for (int i=0;i<10;i++)
{
g_array[i]= ' D ';
Pedit->setwindowtext (G_array);
Sleep (1000);
}
critical_section. Unlock ();
return 0;

}
Double-click the buttons Idc_writew and idc_writed, respectively, and add their response functions:
void Cmultithread8dlg::onwritew ()
{
CWinThread *pwritew=afxbeginthread (Writew,
&M_CTRLW,
Thread_priority_normal,
0,
create_suspended);
Pwritew->resumethread ();
}

void cmultithread8dlg::onwrited ()
{
CWinThread *pwrited=afxbeginthread (writed,
&m_ctrld,
Thread_priority_normal,
0,
create_suspended);
Pwrited->resumethread ();

}
Because the code is simpler, it is no longer detailed. Compile and run the routine, you can click two consecutive buttons to observe the role of the critical class.
B. Using the CEvent class

The CEvent class provides support for events. An event is a synchronization object that allows a thread to wake up another thread when a situation occurs. For example, in some network applications, a thread (recorded as a) is responsible for listening to the communication port, and the other thread (recorded as B) is responsible for updating the user data. By using the CEvent class, thread A can tell thread B when to update user data. Each CEvent object can have two states: signaled state and no signal state. The thread monitors the state of the CEvent class object in which it is located and takes appropriate action at the appropriate time.
In MFC, there are two types of CEvent class objects: Manual events and automatic events. An automatic CEvent object is automatically returned to a signal-free state after it is freed by at least one thread, and when the human event object obtains the signal, it frees the available thread, but until the call member function resetevent () sets it to no-signal state. When you create an object of the CEvent class, an automatic event is created by default. The prototypes and parameters for each member function of the CEvent class are described below:

1, CEvent (BOOL binitiallyown=false,
BOOL Bmanualreset=false,
LPCTSTR Lpszname=null,
Lpsecurity_attributes lpsaattribute=null);
Binitiallyown: Specifies the event object initialization state, true to have a signal, false for no signal;
bManualReset: Specifies whether the event to be created belongs to a human or automatic event. True for manual events, false for automatic events;
The latter two parameters are generally set to null and are not described here.
2, BOOL cevent::setevent ();
Sets the state of the CEvent class object to a signaled state. If the event is a human event, the CEvent class object remains signaled until the call member function resetevent () resets it to a signal-free state. If the CEvent class object is an automatic event, the CEvent class object is automatically reset to a signal-free state by the system after SetEvent () sets the event to a signaled state.

If the function succeeds, a value other than 0 is returned, otherwise zero is returned.

3, BOOL cevent::resetevent ();
This function sets the state of the event to a signal-free state and keeps that state until SetEvent () is called. Because automatic events are automatically reset by the system, automatic events do not need to call this function. If the function succeeds, returns a value other than 0, otherwise returns zero. We typically monitor event status by calling the WaitForSingleObject function. We have described the function earlier. Because of the language description, the CEvent class is difficult to understand, but you can understand it a few times by carefully pondering the following routines.

Routine 9 MultiThread9

Create a dialog-based project MULTITHREAD9, add a button and two edit box controls to the dialog idd_multithread9_dialog, the button has an ID of Idc_writew, and the caption is "Write ' W '"; two edit box IDs are idc_ W and Idc_d, attributes are selected read-only;
Declare two thread functions in a MultiThread9Dlg.h file:
UINT Writew (LPVOID pparam);
UINT writed (LPVOID pparam);
Add CEdit class variables m_ctrlw and M_ctrld to Idc_w and idc_d, respectively, using ClassWizard;
In the MultiThread9Dlg.cpp file, add the following:
In order to correctly use the synchronization class in the file, add at the beginning of the file

#include "Afxmt.h"
Defines an event object and an array of characters, which are defined as global variables, in order to be used among different threads.
CEvent eventwrited;
Char g_array[10];
To add a thread function:
UINT Writew (LPVOID pparam)
{
CEdit *pedit= (cedit*) Pparam;
Pedit->setwindowtext ("");
for (int i=0;i<10;i++)
{
g_array[i]= ' W ';
Pedit->setwindowtext (G_array);
Sleep (1000);
}
Eventwrited.setevent ();
return 0;

}
UINT writed (LPVOID pparam)
{
CEdit *pedit= (cedit*) Pparam;
Pedit->setwindowtext ("");
WaitForSingleObject (Eventwrited.m_hobject,infinite);
for (int i=0;i<10;i++)
{
g_array[i]= ' D ';
Pedit->setwindowtext (G_array);
Sleep (1000);
}
return 0;

}
By carefully analyzing these two thread functions, you will understand the CEvent class correctly. The thread writed executes to WaitForSingleObject (Eventwrited.m_hobject,infinite), waits until the event eventwrited to signal that the line friend down, Because the Eventwrited object is an automatic event, when WaitForSingleObject () returns, the system automatically resets the Eventwrited object to a signal-free state.
Double-click the button IDC_WRITEW to add its response function:
void Cmultithread9dlg::onwritew ()
{
CWinThread *pwritew=afxbeginthread (Writew,
&M_CTRLW,
Thread_priority_normal,
0,
create_suspended);
Pwritew->resumethread ();

CWinThread *pwrited=afxbeginthread (writed,
&m_ctrld,
Thread_priority_normal,
0,
create_suspended);
Pwrited->resumethread ();

}
Compile and run the program, and click the "Write ' W" button to understand what the event object does.
C. Using the CMutex class

A mutex object is much like a critical section object. A mutex object differs from a critical section object in that a mutex can be used between processes, whereas a critical section object can only be used between threads of the same process. Of course, mutexes can also be used between threads of the same process, but in this case, using a critical section saves system resources and is more efficient.

D. Using the CSemaphore class

You can use the semaphore object when you need a counter to limit the number of threads that can be used. The object of the CSemaphore class holds the count value of the thread that is currently accessing a specified resource, which is the number of threads that can currently use the resource. If the count reaches 0, all access attempts to the resources controlled by the CSemaphore class object are placed in a queue until the timeout or count value is not 0 o'clock. When a thread is disposed of a protected resource, the value is reduced by 1, and when a thread completes access to the controlled shared resource, the count is increased by 1. The maximum number of threads that a resource controlled by the CSemaphore class object can concurrently accept access is specified in the object's build function.

The constructor prototypes and parameters for the CSemaphore class are described below:

CSemaphore (LONG linitialcount=1,
LONG Lmaxcount=1,
LPCTSTR Pstrname=null,
Lpsecurity_attributes lpsaattributes=null);
lInitialCount: The initial count value of the semaphore object, which can access the initial value of the number of threads;
Lmaxcount: The maximum value of the semaphore object count value that determines the maximum number of threads that can access the semaphore-protected resource at the same time;
The latter two parameters are generally null in the same process and are not discussed too much;
When you create a semaphore object with the constructor of the CSemaphore class, you also indicate the maximum allowable resource count and the current available resource count. In general, the current available resource count is set to the maximum resource count, and each additional thread accesses the shared resource, the current available resource count is reduced by 1, and the semaphore signal can be emitted as long as the current available resource count is greater than 0. However, the current available count is reduced to 0 o'clock, indicating that the number of threads currently occupying the resource has reached the maximum allowable number and no longer allows other threads to enter, at which point the semaphore signal will not be emitted. After the thread has finished processing the shared resource, it should pass the ReleaseSemaphore () function to add 1 of the currently available resources at the same time as it leaves.

A simple example is given below to illustrate the use of the CSemaphore class.

Routine MULTITHREAD10

Create a dialog-based project MULTITHREAD10, add a button and three edit box controls to the dialog idd_multithread10_dialog, the button's ID is Idc_start, and the caption is "Write ' A ', ' B ', ' C '" ; The IDs of the three edit boxes are idc_a, Idc_b, and Idc_c, respectively, and the attributes are selected read-only;
Declare two thread functions in a MultiThread10Dlg.h file:
UINT Writea (LPVOID pparam);
UINT writeb (LPVOID pparam);
UINT Writec (LPVOID pparam);
Add CEdit class Variables M_ctrla, M_CTRLB, and M_CTRLC to Idc_a, Idc_b, and Idc_c, respectively, using ClassWizard;
In the MultiThread10Dlg.cpp file, add the following:
In order to correctly use the synchronization class in the file, add at the beginning of the file:

#include "Afxmt.h"
Defines a semaphore object and an array of characters, in order to be able to be used among different threads, defined as global variables:
CSemaphore Semaphorewrite (2,2); Resources up to 2 threads, current number of accessible threads 2
Char g_array[10];
Add three thread functions:

UINT Writea (LPVOID pparam)
{
CEdit *pedit= (cedit*) Pparam;
Pedit->setwindowtext ("");
WaitForSingleObject (Semaphorewrite.m_hobject,infinite);
CString str;
for (int i=0;i<10;i++)
{
Pedit->getwindowtext (str);
g_array[i]= ' A ';
Str=str+g_array[i];
Pedit->setwindowtext (str);
Sleep (1000);
}
ReleaseSemaphore (Semaphorewrite.m_hobject,1,null);
return 0;

}
UINT writeb (LPVOID pparam)
{
CEdit *pedit= (cedit*) Pparam;
Pedit->setwindowtext ("");
WaitForSingleObject (Semaphorewrite.m_hobject,infinite);
CString str;
for (int i=0;i<10;i++)
{

Pedit->getwindowtext (str);
g_array[i]= ' B ';
Str=str+g_array[i];
Pedit->setwindowtext (str);
Sleep (1000);
}
ReleaseSemaphore (Semaphorewrite.m_hobject,1,null);
return 0;

}
UINT Writec (LPVOID pparam)
{
CEdit *pedit= (cedit*) Pparam;
Pedit->setwindowtext ("");
WaitForSingleObject (Semaphorewrite.m_hobject,infinite);
for (int i=0;i<10;i++)
{
g_array[i]= ' C ';
Pedit->setwindowtext (G_array);
Sleep (1000);
}
ReleaseSemaphore (Semaphorewrite.m_hobject,1,null);
return 0;

}
These three thread functions no longer say much. In the state where the semaphore object is signaled, the thread executes to the WaitForSingleObject statement to continue execution, while the number of available threads is reduced by 1; If the semaphore object is not signaled when the thread executes to the WaitForSingleObject statement, the thread waits here. Until the semaphore object has a signal line friend down execution.
Double-click the button idc_start to add its response function:
void Cmultithread10dlg::onstart ()
{
CWinThread *pwritea=afxbeginthread (Writea,
&m_ctrla,
Thread_priority_normal,
0,
create_suspended);
Pwritea->resumethread ();

CWinThread *pwriteb=afxbeginthread (Writeb,
&M_CTRLB,
Thread_priority_normal,
0,
create_suspended);
Pwriteb->resumethread ();

CWinThread *pwritec=afxbeginthread (Writec,
&M_CTRLC,
Thread_priority_normal,
0,
create_suspended);
Pwritec->resumethread ();


}
Well, multithreaded programming is introduced here, I hope this article can help you.

VC + + Multithreaded programming-inter-thread communication and thread synchronization

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.