Every system has threads, and the most important role of threads is parallel processing to improve the concurrency rate of software. For the interface, it can also improve the interface response.
Threads are divided into interface threads and worker threads. The interface is actually something drawn by a thread, which maintains a "message queue ", "Message Queue" is also the biggest difference between the interface thread and the worker thread. This word should be in your mind, deep-rooted!
If the interface thread stops somewhere, it means it cannot process the window message, so sometimes we can see that the whole interface has no response. A function called waitforobjectex will be provided to solve this problem. We will discuss it later.
The thread is first created by using the following function: createthread; I will not talk about the specific parameters and check msdn myself. Thread1 is a thread function. A thread function is a global function, as follows:
DWORD winapi thread1 (lpvoid lpparam)
{
While (1)
{
Outputdebugstring ("11111 ");
Sleep (10 );
}
Return 0;
}
// The following statement creates a thread.
Createthread (null, 0, thread1, 0, 0, null );
Of course, we can't let a thread crash. It may lead to some inexplicable problems when you exit the program, or some data loss, or a crash dialog box for you...
Therefore, we need to manage this thread. First, let it exit.
We add a bool variable g_bexitthread to its while. In this way, the thread function becomes like the following:
DWORD winapi thread1 (lpvoid lpparam)
{
While (! G_bexitthread)
{
Outputdebugstring ("11111 ");
Sleep (10 );
}
Return 0;
}
Set g_bexitthread to true when you need it to exit, which means, hello, bro, you should quit.
Of course, we also need to know whether it has successfully exited. Because the thread handle is a kernel object, we need to use Windows waitforsingleobject to wait. When it is created and the code waiting for it to exit is about to change, there is an additional handle g_htrd variable:
// Create
G_bexitthread = false;
G_htrd = createthread (null, 0, thread1, 0, 0, null );
// Wait until the thread ends
G_bexitthread = true;
If (g_htrd! = NULL)
{
DWORD dwret = waitforsingleobject (g_htrd, 5000 );
If (dwret = wait_object_0)
{
Afxmessagebox ("thread exit success! ");
}
Else
{
DWORD dwret = 0;
Getexitcodethread (g_htrd, & dwret );
Terminatethread (g_htrd, dwret );
Afxmessagebox ("thread exit, but not all OK! ");
}
Closehandle (g_htrd );
G_htrd = NULL;
}
As mentioned above, waiting for other threads to end in the interface thread, that is, when waitforsingleobject is used, the processing of the entire window message will be blocked. Therefore, if we want to wait for other kernel objects in the interface thread, we need to use this "Wait a moment to process the interface message" method. I have written a waitforobjectex function, as follows:
// This function can only be used for interface threads
Static DWORD waitforobjectex (handle hhandle, DWORD dwmilliseconds)
{
Bool Bret;
MSG;
Int iwaitret;
Int ntimeout = 0;
While (Bret =: getmessage (& MSG, null, 0, 0 ))! = 0)
{
If (ntimeout ++ * 20> = dwmilliseconds)
Break;
Iwaitret = waitforsingleobject (hhandle, 20 );
If (iwaitret! = Wait_timeout)
{
Break;
}
If (Bret =-1)
{
Break;
}
Else
{
: Translatemessage (& MSG );
: Dispatchmessage (& MSG );
}
}
Return iwaitret;
}
In many cases, we do not want to use a thread as a global function. Therefore, we write a thread as a static member object of a class. Of course, we can't lose the two variables: Exit flag and thread handle. (Set this class to ctestthreaddlg)
// H file
Bool m_bexitthread;
Handle m_htrd;
Static DWORD winapi thread1 (lpvoid lpparam );
// CPP file, this pointer is passed during creation, because the class static member function cannot be a non-static member of the category, without this pointer
// (Knowledge points of C ++)
M_bexitthread = false;
M_htrd = createthread (null, 0, thread1, this, 0, null );
The thread function is changed:
DWORD winapi ctestthreaddlg: thread1 (lpvoid lpparam)
{
Ctestthreaddlg * pdlg = (ctestthreaddlg *) lpparam;
While (! Pdlg-> m_bexitthread)
{
Outputdebugstring ("11111 ");
Sleep (10 );
}
Return 0;
}
When several threads run together, we need to pay attention to the thread synchronization problem. Thread Synchronization generally occurs when multiple threads share resources. For example, when both threads use the same vector, they insert the vector. Unfortunately, the vector is NOT thread-safe and the program will crash at this time, so we need to synchronize the vector resource, which means "you are waiting when I access ". The program is roughly as follows:
DWORD winapi ctestthreaddlg: thread1 (lpvoid lpparam)
{
Ctestthreaddlg * pdlg = (ctestthreaddlg *) lpparam;
While (! Pdlg-> m_bexitthread)
{
Outputdebugstring ("11111 ");
Pdlg-> m_csforvec.lock ();
Pdlg-& gt; m_vectest.push_back ("111 ");
Pdlg-> m_csforvec.unlock ();
Sleep (10 );
}
Return 0;
}
DWORD winapi ctestthreaddlg: thread2 (lpvoid lpparam)
{
Ctestthreaddlg * pdlg = (ctestthreaddlg *) lpparam;
While (! Pdlg-> m_bexitthread2)
{
Outputdebugstring ("222 ");
Pdlg-> m_csforvec.lock ();
Pdlg-& gt; m_vectest.push_back ("222 ");
Pdlg-> m_csforvec.unlock ();
Sleep (10 );
}
Return 0;
}
M_csforvec is a ccriticalsection variable. This synchronization object is different from other synchronization variables (events, semaphores, mutex areas, and so on, for example, only access between threads of the same process and user-mode access in the operating system are allowed. Others must enter the core state. As a result, the core objects in this key zone are about 100 times faster than others...
We have already talked about thread creation, management (exit thread, wait thread), synchronization, etc. What do we find in common? As a programmer, we need to be very sensitive to discover the commonalities in these codes, which is the main prerequisite for code design.
First, we find that the preceding thread has two variables:
Bool m_bexitthread; // indicates the exit of the thread.
Handle m_htrd; // thread handle
In addition, when waitforsingleobject is used, we cannot wait infinitely, so we need to have one more DWORD m_dwwaittimeout;
Since I want to encapsulate thread startup and termination, I have designed these interfaces:
Bool start (lpvoid lpparam); // start the thread. The parameters required by the thread are transmitted from here.
Bool end (); // end the thread
Virtual void run (); // rewrite the run Function
Therefore, the entire thread is encapsulated into the following classes:
// Mythread. h
# Ifndef my_thread_h
# Define my_thread_h
Class cmythread
{
Public:
Cmythread ();
Virtual ~ Cmythread ();
Bool start (lpvoid lpparam );
Bool end ();
Virtual void run ();
Protected:
Static DWORD winapi thread (lpvoid lpparam );
Void runonceend ();
DWORD m_dwwaittimeout;
Bool m_bexitthread;
Handle m_htrd;
Lpvoid m_lpparam;
};
# Endif
// Mythread. cpp
# Include "stdafx. H"
# Include "mythread. H"
//////////////////////////////////////// /////////////////////////////////////
// Cmythread
Cmythread: cmythread ()
{
M_bexitthread = false;
M_htrd = NULL;
M_dwwaittimeout = 5000;
}
Cmythread ::~ Cmythread ()
{
}
Bool cmythread: Start (lpvoid lpparam)
{
M_lpparam = lpparam;
M_bexitthread = false;
M_htrd = createthread (null, 0, thread, this, 0, null );
Return true;
}
Bool cmythread: end ()
{
M_bexitthread = true;
If (m_htrd! = NULL)
{
DWORD dwret = waitforsingleobject (m_htrd, m_dwwaittimeout );
If (dwret = wait_object_0)
{
Afxmessagebox ("thread exit success! ");
}
Else
{
DWORD dwret = 0;
Getexitcodethread (m_htrd, & dwret );
Terminatethread (m_htrd, dwret );
Afxmessagebox ("thread fucking exit! ");
}
Closehandle (m_htrd );
M_htrd = NULL;
}
Return true;
}
DWORD winapi cmythread: thread (lpvoid lpparam)
{
Cmythread * ptrd = (cmythread *) lpparam;
While (! Ptrd-> m_bexitthread)
{
Ptrd-> Run ();
}
Return 0;
}
Void cmythread: runonceend ()
{
M_bexitthread = true;
Closehandle (m_htrd );
M_htrd = NULL;
}
Void cmythread: Run ()
{
}
We need to reload the run function when writing our own thread.
// Derive a class
Class cmythread1: Public cmythread
{
Public:
Virtual void run ();
};
// Rewrite the run Function
Void cmythread1: Run ()
{
Ctestthreaddlg * pdlg = (ctestthreaddlg *) m_lpparam;
Outputdebugstring ("222 ");
Pdlg-> m_csforvec.lock ();
Pdlg-& gt; m_vectest.push_back ("222 ");
Pdlg-> m_csforvec.unlock ();
Sleep (10 );
// If this thread only needs to run once, add the following sentence
Runonceend ();
}
Then, the use of our previous two threads becomes the following form:
Cmythread1 g_t1, g_t2, g_t3;
Void ctestthreaddlg: onbutton3 ()
{
G_t1.start (this );
G_t2.start (this );
G_t3.start (this );
}
Void ctestthreaddlg: onbutton4 ()
{
G_t1.end ();
G_t2.end ();
G_t3.end ();
}
You only need to perform the following steps:
1. derive your own Thread class
2. overload the run Function
3. Call start to start the thread.
4. Call the end thread
Of course, this encapsulation method is my favorite. The purpose of encapsulation is to make it easy to use and hide details. You can also encapsulate the thread usage method based on your preferences, if you can share your achievements here and let me and everyone learn about your design techniques, it's really very good and 3q!