Ten Examples of VC ++ multi-thread programming

Source: Internet
Author: User
Tags export class semaphore

1. Use Win32 API

Handle createthread (lpsecurity_attributes lpthreadattributes,
DWORD dwstacksize,
Lpthread_start_routine lpstartaddress,
Lpvoid lpparameter,
DWORD dwcreationflags,
Lpdword lpthreadid );
This function creates a new thread in the process space of the called process and returns the handle of the created thread. The parameters are described as follows:
Lpthreadattributes: pointer to a security_attributes structure. This structure determines the thread Security Attribute and is generally set to null;
Dwstacksize: Specifies the stack depth of the thread, which is generally set to 0;
Lpstartaddress: the address of the function where the new thread starts to execute the time code, that is, the start address of the thread. Generally, it is (lpthread_start_routine) threadfunc, and threadfunc is the name of the thread function;
Lpparameter: Specifies the 32-bit parameter transmitted to the thread during thread execution, that is, the parameter of the thread function;
Dwcreationflags: controls the additional flag created by the thread. Two values can be used. If this parameter is set to 0, the thread starts execution immediately after it is created. If this parameter is create_suincluded, the thread is suspended and not executed immediately after the system generates a thread, until the function resumethread is called;
Lpthreadid: returns the ID of the created thread;
If creation is successful, the thread handle is returned. Otherwise, null is returned.

The two most important parameters are lpstartaddress, which is the name of the input function. They can only be global or static functions. lpparameter is the parameter of the thread-executed function and is of the lpvoid type.

In fact, the output function is equivalent to two: the handle type and the thread ID.

1.1 first example

A thread function is a global function with no parameter input. It interacts with the main process and window through the Win32 API function (setdlgitemtext, afxgetmainwnd) and global variable (m_brun.

Volatile bool m_brun; // The volatile modifier indicates that the compiler does not need to optimize the variable, that is, it does not need to be put into a register,
// And the value can be changed externally. // Volatile is a very important modifier for global variables referenced by multiple threads. Void threadfunc () {ctime time; cstring strtime; m_brun = true; while (m_brun) {time = ctime: getcurrenttime (); strtime = time. format ("% H: % m: % s");: setdlgitemtext (afxgetmainwnd ()-> m_hwnd, idc_time, strtime); sleep (1000 );}}
void CMultiThread1Dlg::OnStart() {hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&ThreadID);}void CMultiThread1Dlg::OnStop() {m_bRun = FALSE;}

1.2 Example 2

The parameter can be input from the main thread to the thread function, passing the address

void ThreadFunc(int integer){int i;for(i=0;i<integer;i++){Beep(200,50);Sleep(1000);}} 

void CMultiThread2Dlg::OnStart() {UpdateData(TRUE);int integer=m_nCount;hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,(VOID*)m_nCount,  0,&ThreadID);GetDlgItem(IDC_START)->EnableWindow(FALSE);WaitForSingleObject(hThread,INFINITE);GetDlgItem(IDC_START)->EnableWindow(TRUE);}

DWORD waitforsingleobject (handle hhandle, DWORD dwmilliseconds );
Hhandle is the handle of the object to be monitored (usually a synchronization object or a thread;
Dwmilliseconds is the timeout value set by the hhandle object, in milliseconds;
When this function is called in a thread, the thread is temporarily suspended. The system monitors the state of the object indicated by hhandle. If the object waiting by the thread changes to a signal state within milliseconds of the suspended dwmilliseconds, the function returns immediately. If the timeout time has reached dwmilliseconds in milliseconds, however, the object indicated by hhandle has not become in a signal state, and the function returns the result. The dwmilliseconds parameter has two special values: 0 and infinite. If the value is 0, the function returns immediately. If the value is infinite, the thread is suspended until the object indicated by hhandle changes to a signal state.

It is to pause the main thread here. If the thread is not suspended, this function will not end.
This routine calls this function. After pressing the idc_start button, it waits until the thread returns and restores the idc_start button to its normal state.

1.3 The input parameter is a complex structure

threadInfo Info;UINT ThreadFunc(LPVOID lpParam){threadInfo* pInfo=(threadInfo*)lpParam;for(int i=0;i<100;i++){int nTemp=pInfo->nMilliSecond;pInfo->pctrlProgress->SetPos(i);Sleep(nTemp);}return 0;}

void CMultiThread3Dlg::OnStart() {UpdateData(TRUE);Info.nMilliSecond=m_nSecond;Info.pctrlProgress=&m_ctrlPgr;hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,&Info,0,&ThreadID);}


In fact, since these two quantities are both ready-made member variables, you can directly input the this pointer.

void CMultiThread3Dlg::OnStart() {UpdateData(TRUE);//Info.nMilliSecond=m_nSecond;//Info.pctrlProgress=&m_ctrlPgr;hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,this,0,&ThreadID);}

UINT ThreadFunc(LPVOID lpParam){//threadInfo* pInfo=(threadInfo*)lpParam;CMultiThread3Dlg *pInfo = (CMultiThread3Dlg*)lpParam;for(int i=0;i<100;i++){//int nTemp=pInfo->nMilliSecond;//pInfo->pctrlProgress->SetPos(i);int nTemp=pInfo->m_nSecond;pInfo->m_ctrlPgr.SetPos(i);Sleep(nTemp);}return 0;}

1.4 test the maximum number of threads that can be created

I didn't come to any conclusion. The program is just as dead.

2. MFC multi-thread programming

There are two types of threads in MFC, namely the worker thread and the user interface thread. The main difference between the two is thatThe worker thread does not have a message loop, while the user interface thread has its own message queue and message loop.
The worker thread does not have a message mechanism and is usually used to execute background computing and maintenance tasks, such as lengthy computing processes and printer background printing. User Interface threads are generally used to process user input independent of other threads, and respond to events and messages generated by users and systems. However, for Win32 API programming, there is no difference between the two threads. They only need the thread startup address to start the thread to execute the task.


In MFCGlobal function afxbeginthread ()To create and initialize the running of a thread.Two reload ModesTo create the worker thread and user interface thread respectively. The prototype and parameters of the two overloaded functions are described as follows:
(1) cwinthread * afxbeginthread (afx_threadproc pfnthreadproc,
Lpvoid pparam,
Npriority = thread_priority_normal,
Uint nstacksize = 0,
DWORD dwcreateflags = 0,
Lpsecurity_attributes lpsecurityattrs = NULL );
Pfnthreadproc: pointer to the execution function of the worker thread. The prototype of the thread function must be declared as follows:
Uint executingfunction (lpvoid pparam );
Please note that executingfunction () should return a uint type value to indicate the reason for the function to end. Generally, if 0 is returned, the execution is successful.
Pparam: A 32-bit parameter passed to the thread function. The execution function will explain the value in some way. It can be a value, a pointer to a structure, or even ignored;
Npriority: the priority of the thread. If the value is 0, the thread has the same priority as its parent thread;
Nstacksize: the size of the stack allocated by the thread, in bytes. If nstacksize is set to 0, the thread stack is set to the same size as the parent thread stack;
Dwcreateflags: If it is 0, the thread starts execution immediately after creation. If it is create_suspend, the thread is suspended immediately after creation;
Lpsecurityattrs: Specifies the thread's Security Attribute pointer, which is generally null;
(2) cwinthread * afxbeginthread (cruntimeclass * pthreadclass,
Int npriority = thread_priority_normal,
Uint nstacksize = 0,
DWORD dwcreateflags = 0,
Lpsecurity_attributes lpsecurityattrs = NULL );
Pthreadclass isPointer to the runtime Class Object of an export class of cwinthreadThe export class defines the startup and exit of the created user interface thread. Other parameters have the same meaning as 1. The thread generated using the prototype of the function also has a message mechanism. In future examples, we will find that the mechanism is almost the same as that of the main thread.
Below we will briefly describe the data members and common functions of the cwinthread class:
M_hthread: handle of the current thread;
M_nthreadid: ID of the current thread;
M_pmainwnd: pointer to the main application window
Bool cwinthread: createthread (DWORD dwcreateflags = 0, uint nstacksize = 0, lpsecurity_attributes lpsecurityattrs = NULL );
The dwcreateflags, nstacksize, and lpsecurityattrs parameters in this function have the same meaning as the corresponding parameters in the API function createthread. If the function is successfully executed, a non-0 value is returned; otherwise, 0 is returned. Generally, afxbeginthread () is called to create and start a thread at a time, but a two-step method can also be used to create a thread: first, create an object of the cwinthread class, then, call the createthread () member function of the object to start the thread.

Virtual bool cwinthread: initinstance ();
Reload this function to control the initialization of the user interface thread instance. If Initialization is successful, a non-0 value is returned; otherwise, 0 is returned. The User Interface thread often reloads this function. The worker thread generally does not use initinstance ().
Virtual int cwinthread: exitinstance ();
Reload the function before the end of the thread to perform some necessary cleaning. This function returns the exit code of the thread. 0 indicates that the execution is successful, and a non-0 value indicates various errors. Like the initinstance () member function, this function is only applicable to user interface threads.

2.1 User Interface thread

Derive a class from cwinthread, rewrite its initialization function, and change the protective type of the constructor to public.

BOOL UiThread::InitInstance(){CFrameWnd* wnd=new CFrameWnd;wnd->Create(NULL,"UI Thread Window");wnd->ShowWindow(SW_SHOW);wnd->UpdateWindow();m_pMainWnd=wnd;return TRUE;}

Create a new thread in the Main Window

void CUIThreadDlg::OnButton1() {UiThread* pThread=new UiThread();pThread->CreateThread();}

The running result is that a new window is created and displayed.

2.2 first define a dialog box and bind it to a thread

BOOL CUIThread::InitInstance(){m_dlg.Create(IDD_UITHREADDLG);m_dlg.ShowWindow(SW_SHOW);m_pMainWnd=&m_dlg;return TRUE;}int CUIThread::ExitInstance(){m_dlg.DestroyWindow();return CWinThread::ExitInstance();}

This time, the afxbeginthread is used to create a thread.

void CMultiThread6Dlg::OnButton1() {AfxBeginThread(RUNTIME_CLASS(CUIThread));}

3. Inter-thread Communication

(1) Use global variables for communication
Since each thread of the same process shares the resources allocated by the operating system, the simplest way to solve inter-thread communication is to use global variables. The volatile modifier is required for standard global variables, which tells the compiler that no optimization is required for the variable, that is, it does not need to be put into a register, and the value can be changed externally. If the information to be transmitted between threads is complex, we can define a structure to transmit information by passing a pointer to the structure.
(2) Use custom messages
We can send custom messages to another thread in the execution function of one thread for communication purpose. A thread sends messages to another thread through the operating system. With the message-driven mechanism of the Windows operating system, when a thread sends a message, the operating system first receives the message and then forwards the message to the target thread, the thread that receives the message must have a message loop.

3.1 Use custom messages for communication

Custom two messages

#define WM_DISPLAY WM_USER+102
#define WM_CALCULATE WM_USER+101 

Then, process custom messages separately.

LRESULT CMultiThread7Dlg::OnDisplay(WPARAM wParam,LPARAM lParam){int nTemp=(int)wParam;SetDlgItemInt(IDC_STATUS,nTemp,FALSE);  return 0;}

LONG CCalculateThread::OnCalculate(UINT wParam,LONG lParam){int nTmpt=0;for(int i=0;i<=(int)wParam;i++){nTmpt=nTmpt+i;}Sleep(500);    ::PostMessage((HWND)(GetMainWnd()->GetSafeHwnd()),WM_DISPLAY,nTmpt,NULL);return 0;}

Message sending functions:

void CMultiThread7Dlg::OnSum() {m_pCalculateThread=(CCalculateThread*)AfxBeginThread(RUNTIME_CLASS(CCalculateThread));Sleep(500);m_pCalculateThread->PostThreadMessage(WM_CALCULATE,nAddend,NULL);}

LONG CCalculateThread::OnCalculate(UINT wParam,LONG lParam){int nTmpt=0;for(int i=0;i<=(int)wParam;i++){nTmpt=nTmpt+i;}Sleep(500);    ::PostMessage((HWND)(GetMainWnd()->GetSafeHwnd()),WM_DISPLAY,nTmpt,NULL);return 0;}

It can be seen that there is no interface for user interface functions, but it is responsible for sending messages after processing the business logic.

4. Thread Synchronization

4.1 Use the critical section

When multiple threads access a dedicated shared resource, you can use the "critical section" object. At any time, only one thread can have an object in the critical section. threads in the critical section can access protected resources or code segments. Other threads wishing to enter the critical section will be suspended and waiting, this ensures that multiple threads do not access Shared resources at the same time.

The procedure for using the ccriticalsection class is as follows:

Define one of the ccriticalsection classesGlobal Object(To make each thread accessible), such as ccriticalsection critical_section;
Call the member lock () of the ccriticalsection class to obtain the critical zone object critical_section.lock () before accessing the resources or code to be protected ();
This function is called in the thread to obtain the critical section requested by the thread. If no other thread occupies the critical zone object at this time, the thread that calls lock () gets the critical zone; otherwise, the thread will be suspended and put into a system queue for waiting, until the thread with a critical section has been released.
After the access to the critical section is complete, use the member function unlock () of the ccriticalsection to release the critical section: critical_section.unlock ();
In other words, thread a executes the critical_section.lock () statement, if other threads (B) are executing the critical_section.lock (); Statement and critical_section. unlock (); the statement before the statement, thread a will wait until thread B finishes executing critical_section. unlock (); statement, thread a will continue to execute.

The thread function is:

Uint writew (lpvoid pparam) {cedit * pedit = (cedit *) pparam; pedit-> setwindowtext (""); critical_section.lock (); // lock the critical section, when other threads encounter critical_section.lock (); when the statement is executed, wait // until the critical_section.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 the critical section. Other threads encounter critical_section.lock (); the statement must wait // until the critical_section.unlock (); statement for (INT I = 0; I <10; I ++) {g_array [I] = 'D '; pedit-> setwindowtext (g_array); sleep (1000);} critical_section.unlock (); Return 0 ;}


Use MFC to start the working thread

void CMultiThread8Dlg::OnWritew() {CWinThread *pWriteW=AfxBeginThread(WriteW,&m_ctrlW,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);pWriteW->ResumeThread();}void CMultiThread8Dlg::OnWriter() {CWinThread *pWriteD=AfxBeginThread(WriteD,&m_ctrlR,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);pWriteD->ResumeThread();}

The effect is to write 10, and then write 10, because global variables

4.2 Use the event class

The cevent class provides event support. An event is a synchronization object that allows a thread to wake up in a certain situation. For example, in some network applications, one thread (as a) is responsible for listening to the communication port, and the other thread (as B) is responsible for updating user data. By using the cevent class, thread a can notify thread B when to update user data. Each cevent object can have two states: Signal State and no signal state. The thread monitors the status of the cevent Class Object in it, and takes corresponding actions accordingly.

In MFC, there are two types of cevent objects: manual events and automatic events. An automatic cevent object is automatically returned to the non-signal state after at least one thread is released. However, after the human event object obtains the signal, it releases the available thread until it calls the member function resetevent () set it to the stateless state. When you create a cevent Class Object, automatic events are created by default.

The prototype and parameters of the member functions of the cevent class are described as follows:
1. cevent (bool binitiallyown = false, bool bmanualreset = false, lpctstr lpszname = NULL, lpsecurity_attributes lpsaattribute = NULL );
Binitiallyown: Specifies the initialization status of the event object. True indicates a signal, and false indicates no signal;
Bmanualreset: Specifies whether the event to be created is a manual event or an automatic event. True indicates a manual event, and false indicates an automatic event;
The last two parameters are generally set to null, which is not described here.
2. bool cevent: setevent ();
Set the status of the cevent class object to a signal state. If the event is a manual event, the cevent class object remains in a signal State until the member function resetevent () is called to reset it to a non-signal state. If the cevent class object is an automatic event, after setevent () sets the event to a signal state, the cevent class object is automatically reset from the system to a non-signal state. If the function is successfully executed, a non-zero value is returned. Otherwise, zero is returned.
3. bool cevent: resetevent ();
This function sets the event status to stateless, and maintains the status 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 is successfully executed, a non-zero value is returned. Otherwise, zero is returned. We generally call the waitforsingleobject function to monitor the event status. We have already introduced this function. Because of the language description, the understanding of the cevent class is indeed difficult, but you only need to carefully taste the following routine and read it several times.

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;}

void CMultiThread9Dlg::OnWriter() {CWinThread *pWriteW=AfxBeginThread(WriteW,&m_ctrlR,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);pWriteW->ResumeThread();}void CMultiThread9Dlg::OnWrited() {CWinThread *pWriteD=AfxBeginThread(WriteD,&m_ctrlD,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);pWriteD->ResumeThread();}

It is also a global variable and must be used with waitforsingelobject.

4.3Use csemaphore class

When a counter is required to limit the number of threads that can be used, the "semaphore" object can be used. The csemaphore class object stores the Count value of the thread that currently accesses a specified resource. The Count value is the number of threads that can still use this resource. If the Count reaches zero, all access attempts to the resources controlled by the csemaphore class object will be put into a queue for waiting until the timeout or the Count value is not zero. When a thread is released and has accessed the protected resource, the Count value is reduced by 1. When a thread completes access to the controlled shared resource, the Count value increases by 1. Csemaphore
The maximum number of threads allowed to access resources controlled by class objects is specified in the object's build function.


The constructor prototype and parameters of the csemaphore class are described as follows:
Csemaphore (long linitialcount = 1, long lmaxcount = 1, lpctstr pstrname = NULL, lpsecurity_attributes lpsaattributes = NULL );
Linitialcount: the initial count value of the semaphore object to access the initial value of the number of threads;
Lmaxcount: Maximum number of threads that can access resources protected by semaphores at the same time;
The latter two parameters are generally null in the same process and are not discussed too much;
When using the csemaphore constructor to create a semaphore object, you must specify the maximum allowed resource count and the current available resource count. Generally, the current available resource count is set to the maximum Resource Count. Each time a thread is added to access a shared resource, the current available resource count is reduced by 1, as long as the current available resource count is greater than 0, a semaphore signal can be sent. However, when the current available count is reduced to 0, it indicates that the number of threads currently occupying resources has reached the maximum allowed number, and other threads cannot enter, at this time, the semaphore signal cannot be sent. After processing shared resources, the thread should use the releasesemaphore () function to add 1 to the number of currently available resources while leaving.


Http://download.csdn.net/detail/masofeng/4130053


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.