進程和線程的關係
程序→運行→進程
一個進程至少有一個線程,一個進程能夠有多個線程
簡單的程序設計,一個進程一個線程,即主線程,若主線程須要進行大量運算時,會影響對話框的其餘操做,表現爲程序未響應的阻塞狀態。
高級的程序設計,一個進程多個線程
1、 建立(啓動)線程
//建立線程函數
AfxBeginThread()
//線程函數
typedef UINT(AFX_CDECL *AFX THREADPROC)(LPVOID);編程
在VS上利用基於對話框的MFC編程,創建名爲Demo的項目,以下窗口:函數
其中,從左到右的靜態文本框,分別改ID爲:IDC_OUTPUT_一、IDC_OUTPUT_二、IDC_OUTPUT_3。按鈕默認。
一、button1直接用主線程
在Demodlg.cpp中修改button1函數
//直接用主線程
void CDemoDlg::OnBnClickedButton1()
{
// TODO:在此添加控件通知處理程序代碼
for(int i=1; i<=100; i++)
{
SetDlgItemInt(IDC_OUTPUT_1, i);
Sleep(1000);//等待1s
}
}線程
運行:結果在點擊button1後,程序發生阻塞以下圖:設計
二、button2中調用線程函數:
在Demodlg.h中聲明線程函數(必須是全局):
//線程函數的名字能夠任意,可是返回類型和參數必須以下
UINT DemoA(LPVOID pParam);指針
// CDemoDlg對話框
class CDemoDlg: public CDialogEx
{
....
}對象
在Demodlg.cpp中定義線程函數
UINT DemoA(LPVOID pParam)
{
for(int m=1; m<=100; m++)
{
//全局調用,得到主窗口的句柄
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT_2, m,false);
Sleep(500);
}
return 0:
}進程
在Demodlg.cpp中修改button2函數
void CDemoDlg::OnBnClickedButton2()
{
// TODO:在此添加控件通知處理程序代碼
//調用線程
AfxBeginThread (DemoA, NULL);//NULL:不傳遞線程函數的pParam參數,pParam參數
}事件
此次運行後,就能夠操做其餘事件,不會發生阻塞。
三、button3中再調用線程函數DemoB:
建立方法相似,再也不贅述。運行後button二、button3能獨立操做。
補充:
SetDlgItemInt函數的定義以下,該函數將uValue的值設爲對話框控件的文本:
/*
(1)hDlg:指向對話框窗口的句柄。
(2)nIDDlgItem:要改變其文本的控件ID。
(3)uValue:指向要設置的值。
(4)bSigned:指定uValue是否爲一個有符號的值。
*/
BOOL SetDlgItemInt(HWND hDlg,int nIDDlgItem,UINT uValue,BOOL bSigned);資源
2、 線程控制
線程的控制主要有:啓動線程、暫停(掛起)線程、繼續線程、終止線程。
將案例一的窗口修改以下,以前的button2改成啓動,依次再添加暫停、繼續、中止鍵:同步
在Demodlg.cpp中定義全局變量用於保存線程指針:
CWinThread* g_pThreadA;
在Demodlg.cpp構造函數中初始化指針爲NULL
CDemoDlg::CDemoDlg(CWnda pParent /*-NULL*/)
:CDialogEx(CDemoDlg::IDD, pParent)
{
mhIcon = AfxGetApp()->LoadIcon(IDR_MAINPRAME);
g_ThreadA = NULL:
}
在Demodlg.cpp中完善案例一button2中的線程啓動函數:
void CDemoDlg::OnBnClickedButton2()
{
// TODO:在此添加控件通知處理程序代碼]
if (g_pThreadA == NULL)
{
g_pThreadA = AfxBeginThread(DemoA, NULL);
}
else
{
AfxMessageBox(T("線程已經啓動!"));
}
}
一、暫停線程
在Demodlg.cpp中修改「暫停」按鈕事件函數:
void CDemoDlg::OnBnClickedButtonSuspend()
{
// TODO:在此添加控件通知處理程序代碼
if(g_pThreadA == NULL)
{
AfxMessageBox(T("線程不存在!"));
}
else
{
g-pThreadA->SuspendThread(); //暫停,掛起
}
}
二、繼續線程
在Demodlg.cpp中修改「繼續」按鈕事件函數:
void CDemoDlg::OnBnClickedButtonResume()
{
// TODO:在此添加控件通知處理程序代碼
if(g_pThreadA == NULL)
{
AfxMessageBox(T("線程不存在!"));
}
else
{
g-pThreadA->ResumeThread(); //暫停,掛起
}
}
三、終止線程
在Demodlg.cpp中再定義一個BOOL類型的全局變量
CWinThread* g_pThreadA;
BOOL g_bRunningA;
在Demodlg.cpp構造函數中初始化g_bRunningA變量
CDemoDlg::CDemoDlg(CWnda pParent /*-NULL*/)
:CDialogEx(CDemoDlg::IDD, pParent)
{
mhIcon = AfxGetApp()->LoadIcon(IDR_MAINPRAME);
g_ThreadA = NULL:
g_bRunningA = FALSE;
}
在Demodlg.cpp中完善button2按鈕的啓動函數
void CDemoDlg::OnBnClickedButton2()
{
// TODO:在此添加控件通知處理程序代碼]
if (g_pThreadA == NULL)
{
g_pThreadA = AfxBeginThread(DemoA, NULL);
g_bRunningA = TRUE;
}
else
{
AfxMessageBox(T("線程已經啓動!"));
}
}
在Demodlg.cpp中修改「終止」按鈕事件函數:
void CDemoDlg::OnBnClickedEnd()
{
if(g_pThreadA == NULL)
{
AfxMessageBox(T("線程不存在!"));
}
else
{
g_bRunningA = FALSE;
}
}
在Demodlg.cpp中修改線程函數:
UINT DemoA(LPVOID pParam)
{
for(int m=1; m<=100; m++)
{
if(!g_bRunningA) break;
//全局調用,得到主窗口的句柄
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT_2, m,false);
Sleep(500);
}
g_pThreadA = NULL;
g_bRunningA = FALSE;
return 0:
}
運行:可以暫停、繼續、終止線程。
3、 線程同步
線程同步:一次只容許一個線程對共享資源進行操做。
在VS上利用基於對話框的MFC編程,創建名爲Demo的項目,以下窗口:
一、創建第一個線程
在Demodlg.h中聲明線程函數:
//ThreadProcA爲函數名,能夠任意
UINT ThreadProcA(LPVDID pParam);
// CDemoDlg對話框
class CDemoDlg: public CDialogEx
{
....
}
在Demodlg.cpp中定義線程函數:
//定義兩個全局變量做爲共享資源
int k = 1;
int total = 0:
UINT ThreadProcA(LPVOID pParam)
{
for(int i=1; i<=100000000; i++)
{
k = k*2:
k = k/2;
total += k;
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
在Demodlg.cpp中修改button1的事件函數調用線程:
void CDemoDlg::OnBnClickedButton1()
{
// TODO:在此添加控件通知處理程序代碼
AfxBeginThread(ThreadProcA, NULL);
}
二、新建按鈕調用第二個線程函數
在Demodlg.h中聲明線程函數ThreadProcB:
//ThreadProcA爲函數名,能夠任意
UINT ThreadProcA(LPVDID pParam);
//ThreadProcB爲函數名,能夠任意
UINT ThreadProcB(LPVDID pParam);
// CDemoDlg對話框
class CDemoDlg: public CDialogEx
{
....
}
在Demodlg.cpp中定義線程函數ThreadProcB:
UINT ThreadProcB(LPVOID pParam)
{
for(int j=1; j<=100000000; j++)
{
k = k*2:
k = k/2;
total += k;
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
在Demodlg.cpp中修改button2的事件函數調用線程:
void CDemoDlg::OnBnClickedButton2()
{
// TODO:在此添加控件通知處理程序代碼
AfxBeginThread(ThreadProcB, NULL);
}
運行結果:
若是按完button1後,一段時間後按button2,則顯示200000000,符合預期;
若是按完button1後,當即按button2,顯示結果不可預知。這是由於另外一個按鈕button2用了button1線程函數中k,total變量的中間結果。
分析:
下述線程函數的循環體爲臨界區域代碼:
k = k*2:
k = k/2;
total += k;
每一個線程函數進入臨界區域代碼前應該加鎖,執行完臨界區域代碼後要解鎖。
三、線程同步的方法
線程同步的方法主要有4中:臨界區CCriticalSection、互斥量CMutex、信號量CSemahore、事件CEvent
①臨界區(關鍵區)
在Demodlg.cpp中定義全局指針變量g_pCS指向臨界區對象:
CCriticalSection* g_pCS;
在Demodlg.cpp構造函數中建立臨界區對象
CDemoDlg::CDemoDlg(CWnda pParent /*-NULL*/)
:CDialogEx(CDemoDlg::IDD, pParent)
{
mhIcon = AfxGetApp()->LoadIcon(IDR_MAINPRAME);
g_pCS = new CCriticalSection();//有new就要有delete
}
在Demodlg.h中聲明析構函數~CDemoDlg():
class CDemoDlg: public CDialogEx
{
//構造
public:
CDemoDlg(CWnd* pParent = NULL) ; //標準構造函數
~CDemoDlg();//析構函數
//對話框數據
enum { IDD = IDD_DEMO_DIALOG };
.......
}
在Demodlg.cpp中定義析構函數~CDemoDlg():
CDemoDlg::~CDemoDlg()
{
delete g_pCS;
g_pCS = NULL;
}
在Demodlg.cpp線程函數中給臨界區代碼加鎖:
//線程一
UINT ThreadProcA(LPVOID pParam)
{
for(int i=1; i<=100000000; i++)
{
g_pCS->Lock();//加鎖
k = k*2:
k = k/2;
total += k;
g_pCS->Unlock();//解鎖
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
//線程二
UINT ThreadProcB(LPVOID pParam)
{
for(int j=1; j<=100000000; j++)
{
g_pCS->Lock();//加鎖
k = k*2:
k = k/2;
total += k;
g_pCS->Unlock();//解鎖
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
結果:運行速度變慢,但結果正確。是4種方法中運算最快的!
②互斥量
在Demodlg.cpp中定義全局指針變量g_pMutex指向互斥量對象:
CMutex* g_pMutex;
在Demodlg.cpp構造函數中建立互斥量對象
CDemoDlg::CDemoDlg(CWnda pParent /*-NULL*/)
:CDialogEx(CDemoDlg::IDD, pParent)
{
mhIcon = AfxGetApp()->LoadIcon(IDR_MAINPRAME);
g_pMutex = new CMutex();//有new就要有delete
}
在Demodlg.h中聲明析構函數~CDemoDlg():
class CDemoDlg: public CDialogEx
{
//構造
public:
CDemoDlg(CWnd* pParent = NULL) ; //標準構造函數
~CDemoDlg();//析構函數
//對話框數據
enum { IDD = IDD_DEMO_DIALOG };
.......
}
在Demodlg.cpp中定義析構函數~CDemoDlg():
CDemoDlg::~CDemoDlg()
{
delete g_pMutex;
g_pMutex = NULL;
}
在Demodlg.cpp線程函數中給臨界區代碼加鎖:
//線程一
UINT ThreadProcA(LPVOID pParam)
{
CSingleLock singleLock(g_pMutex) ;
for(int j=1; j<=100000000; j++)
{
singleLock.Lock();
if(singleLock.IsLocked())
{
k = k*2:
k = k/2;
total += k;
singleLock.Unlock();
}
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
//線程二
UINT ThreadProcB(LPVOID pParam)
{
CSingleLock singleLock(g_pMutex) ;
for(int i=1; i<=100000000; i++)
{
singleLock.Lock();
if(singleLock.IsLocked())
{
k = k*2:
k = k/2;
total += k;
singleLock.Unlock();
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
運行結果:顯示結果正確,運算特別慢。
③信號量
在Demodlg.cpp中定義全局指針變量g_pSemaphore指向信號量對象:
CSemaphore* g_pSemaphore;
在Demodlg.cpp構造函數中建立信號量對象
CDemoD1g::CDemoDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CDemoDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR MAINFRAME):
g_pSemaphore = new CSemaphore(1,1);//初始計數是1,最大計數是1,實際上信號量很複雜,能夠用於複雜的線程同步。
}
在Demodlg.h中聲明析構函數~CDemoDlg():
class CDemoDlg: public CDialogEx
{
//構造
public:
CDemoDlg(CWnd* pParent = NULL) ; //標準構造函數
~CDemoDlg();//析構函數
//對話框數據
enum { IDD = IDD_DEMO_DIALOG };
.......
}
在Demodlg.cpp中定義析構函數~CDemoDlg():
CDemoDlg::~CDemoDlg()
{
delete g_pSemaphore;
g_pSemaphore = NULL;
}
在Demodlg.cpp線程函數中給臨界區代碼加鎖,和互斥量代碼很是類似:
//線程一
UINT ThreadProcA(LPVOID pParam)
{
CSingleLock singleLock(g_pSemaphore) ;
for(int j=1; j<=100000000; j++)
{
singleLock.Lock();
if(singleLock.IsLocked())
{
k = k*2:
k = k/2;
total += k;
singleLock.Unlock();
}
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
//線程二
UINT ThreadProcB(LPVOID pParam)
{
CSingleLock singleLock(g_pSemaphore) ;
for(int i=1; i<=100000000; i++)
{
singleLock.Lock();
if(singleLock.IsLocked())
{
k = k*2:
k = k/2;
total += k;
singleLock.Unlock();
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
運行結果:顯示結果正確,運算特別慢。
④事件
在Demodlg.cpp中定義全局指針變量g_pEvent指向事件對象:
CEvent* g_pEvent;
在Demodlg.cpp構造函數中建立事件對象
CDemoD1g::CDemoDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CDemoDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR MAINFRAME):
g_pEvent = new CEvent(TRUE);
}
在Demodlg.h中聲明析構函數~CDemoDlg():
class CDemoDlg: public CDialogEx
{
//構造
public:
CDemoDlg(CWnd* pParent = NULL) ; //標準構造函數
~CDemoDlg();//析構函數
//對話框數據
enum { IDD = IDD_DEMO_DIALOG };
.......
}
在Demodlg.cpp中定義析構函數~CDemoDlg():
CDemoDlg::~CDemoDlg()
{
delete g_pEvent;
g_pEvent = NULL;
}
在Demodlg.cpp線程函數中給臨界區代碼加鎖:
//線程一
UINT ThreadProcA(LPVOID pParam)
{
CSingleLock singleLock(g_pEvent) ;
for(int j=1; j<=100000000; j++)
{
singleLock.Lock();
if(singleLock.IsLocked())
{
k = k*2:
k = k/2;
total += k;
singleLock.Unlock();
g_pEvent->SetEvent();//發信號給其餘線程
}
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
//線程二
UINT ThreadProcB(LPVOID pParam)
{
CSingleLock singleLock(g_pEvent) ;
for(int i=1; i<=100000000; i++)
{
singleLock.Lock();
if(singleLock.IsLocked())
{
k = k*2:
k = k/2;
total += k;
singleLock.Unlock();
g_pEvent->SetEvent();//發信號給其餘線程
}
::SetDlgItemInt(AfxGetApp()->mpMainWnd->mhWnd, IDC_OUTPUT, total,false);
return 0;
}
運行結果:顯示結果正確,運算特別慢。
4種方法種最快的是臨界區對象。
做者:X_xxieRiemann 連接:https://www.jianshu.com/p/07fcc8fcaec7 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。