VC++学习(15):多线程
1.?????? 程序,進程,線程
A: 程序是計算機指令的集合,它以文件的形式存儲在磁盤上,而進程通常被定義為一個正在運行的程序的實例,是一個程序在其自身的地址空間中的一次執行活動.一個程序可以對應多個進程.
進程是資源申請,高度和獨立運行的單位,因此,它使用系統中的運行資源,而程序不能申請系統資源,不能被系統高度也不能作為獨立運行的單位,因此它不占系統運行資源.
進程組成:
<1>?? 操作系統用來管理進行的內核對象
???????? 內核對象也是系統用來存放關于進程的統計信息的地方.內核對象是操作系統內部分配的一個內在塊,該內存塊是一種數據結構,其成員負責維護該對象的各種信息.
<2>?? 地址空間
???????? 它包含所有可執行模塊或DLL模塊的代碼和數據.另外,它也包含動態內存分配的空間,例如線程的棧和堆分配空間
B: 進程從來不執行任何東西,它只是純種的容器,若要使進行完成某項操作,它必須擁有一個在它的環境中運行的純種,此線程負責執行包含在進程的地址空間的中的代碼.也就是,真正完成代碼執行的是線程,而進行只是純種的容器,或者說是線程的執行環境.
單個進程可能包含若干個純種,這些線程都”同時”執行進行地址空間的中代碼,每個線程至少擁有一個純種,來執行進行的地址空間中的代碼,當創建一個進程時,操作系統會自動創建這個進程的第一個線程,稱為主純種.也就是執行main函數或WinMain函數的線程,可以把main函數或WinMain函數看作是主線程的入口點函數.此后,主線程可以創建其它的線程.
C: 線程也由兩部分組成:
<1> 線程內核對象,操作系統用它來對線程實施管理,內核對象也是系統用來存放線程統計信息的地地方
<2>線程棧stack:它用于維護線程在執行代碼時需要的所有函數參數和局部變量.線程總是在某個進程環境中創建,系統從進程的地址空間中分配內存,供線程的棧使用.
線程運行:
操作系統為每一個運行線程安排一定的CPU時間—時間片,系統通過一種循環的方式為線程提供時間片,線程在自己的時間內運行,因時間片相當短,因此給用戶的感覺就是好像多個線程是同時運行一樣.
2.?????? 創建線程的SDK函數
HANDLE CreateThread (
?SEC_ATTRS SecurityAttributes,//安全結構全指針,NULL為默認安全性
?ULONG StackSize,//線程初始棧的大小 ,以字節為單位,如果為0或小于0,默認將使用
//與調用該線程相同的棧空間大小
?SEC_THREAD_START StartFunction,//線程處理函數地址(可用函數名)
?PVOID ThreadParameter,//可以為數據或其它信息的指針,表示給新線程傳送參數
?ULONG CreationFlags,//線程創建的標記,可以為0或CREATE_SUSPENDED,如果
//CREATE_SUSPENDED線程創建后暫停狀態,直到程序調用//ResumeThread,如果為0立即運行
?PULONG ThreadId //[out]返回值,系統分配新的線程ID
);
3.??? 一個簡單的多線程程序,模擬售票系統
| #include <windows.h>//必要的頭文件,使用Windows API函數 #include <iostream.h> int index = 0; int tickets = 100;//票數 HANDLE hMutex; //使用全局的互斥對象來保證對同一資源的互斥訪問與操作這里是tickets //線程處理函數原型,形式可從MSDN中拷貝 //線程1 的入口函數 DWORD WINAPI Fun1Proc( ???????? LPVOID lpParameter?? // thread data ); DWORD WINAPI Fun2Proc( ???????? LPVOID lpParameter?? // thread data ); void main(){ ???????? HANDLE hThread1; ???????? DWORD thread1ID; ???? //創建線程1 ???????? hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, &thread1ID); ???????? HANDLE hThread2; ???????? DWORD thread2ID; ???????? //創建線程2 ???????? hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, &thread2ID); ???????? CloseHandle(hThread1); //關閉線程的句柄,為什么要關閉?它將線程的使用計數減1 CloseHandle(hThread2);//這樣當線程結束時,線程內核對象被釋放, //否則只有當進程結束,才釋放線程的內核對象hThread1與hThread2 ??? //創建一個互斥對象,如果成功返回互斥對象的句柄,否則返回NULL ???????? hMutex = CreateMutex(NULL, FALSE, "tickets"); ???????? if (hMutex) ???????? { ?????????????????? if(ERROR_ALREADY_EXISTS == GetLastError()) ?????????????????? { ??????????????????????????? cout << "only one instance can run!" << endl; ??????????????????????????? return; ?????????????????? } ???????? } // ???? while(index++ < 100) // ???? { // ?????????????? cout << "main Thread is running!" << endl; // ???? } ???????? Sleep(4000);//主線程睡眠4秒鐘,給其它線程運行的時間,因為一旦主線程退出則進行退出,其它線程也將退出 } DWORD WINAPI Fun1Proc( ??????????????????????????????????????????????????????? LPVOID lpParameter?? // thread data ?????????????????? ???????????????????????????????????? ){ // ???? while(index++ < 100) // ?????????????? cout << "Thread1 is running!" + index << endl; ???????? ???????? while(TRUE){ ?????????????????? WaitForSingleObject(hMutex, INFINITE);//如果全局互斥對象是有信號狀態,則獲得該對象, //直到調用ReleaseMutex之前,互斥對象是無信號狀態,其它線程不能對互斥對象進行訪問 ?????????????????? if(tickets > 0) ?????????????????? { ??????????????????????????? Sleep(1); ??????????????????????????? cout << "Thread1 sell tickets : " << tickets-- << endl; ?????????????????? } ?????????????????? else ??????????????????????????? break; ?????????????????? ReleaseMutex(hMutex);//將互斥對象設置為有信號狀態 ???????? } ???????? return 0; } DWORD WINAPI Fun2Proc( ???????? LPVOID lpParameter?? // thread data ) {?????? ???????? while(TRUE){ ?????????????????? WaitForSingleObject(hMutex, INFINITE); ?????????????????? if (tickets > 0) ?????????????????? { ??????????????????????????? Sleep(1); ??????????????????????????? cout << "Thread2 sell tickets : " << tickets-- << endl; ?????????????????? } ?????????????????? else ??????????????????????????? break; ?????????????????? ReleaseMutex(hMutex); ???????? } ???????? return 0; } |
?
3.?????? 多線程聊天程序
(1)????? 加載套接字庫在InitInstance()中,調用AfxSocketInit(),此時可以不加載庫文件,但要加入Afxsock.h"頭文件
if (!AfxSocketInit())
?????? {
????????????? AfxMessageBox("加載套接字庫失敗!");
????????????? return FALSE;
?????? }
(2)????? 在CChatDlg.h中類的聲明外,創建一個全局的結構體,包含套接字和窗口的句柄值,主要是在投送消息時可以將兩個需要傳送的消息同時發送
struct RECVPARAM{
?????? SOCKET socket;
?????? HWND hWnd;
};
(3)????? 在CChatDlg中創建成員變量m_socket,然后增加一個成員函數,IniSocket(),在其中完成m_socket的初始化和綁定。
| BOOL CChatDlg::InitSocket() { ???????? m_socket = socket(AF_INET, SOCK_DGRAM, 0); ???????? if (INVALID_SOCKET == m_socket) ???????? { ?????????????????? MessageBox("創建套接字失敗!"); ?????????????????? return FALSE; ???????? } ???????? SOCKADDR_IN addrSock; ???????? addrSock.sin_addr.S_un.S_addr = htonl(INADDR_ANY); ???????? addrSock.sin_family = AF_INET; ???????? addrSock.sin_port = htons(6010); ???????? ???????? int bindRst; ???????? bindRst = bind(m_socket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR)); ???????? if (SOCKET_ERROR == bindRst) ???????? { ?????????????????? closesocket(m_socket); ?????????????????? MessageBox("綁定失敗!"); ?????????????????? return FALSE; ???????? } ???????? return TRUE; } |
(4)????? .創建一個線程,CreateThread(),須將線程函數RecvProc定義為靜態的或者全局函數。因為對于運行的代碼來說,它不知道要產生哪一個對象,即運行時根本不知道如何去產生一個CChatDialog類的對象,對于運行時代碼來說,如果要調用純種函數來啟動某個純種的話,應該不需要產生某個對象就可以調用這個純種函數.因此要定義為類的靜態成員或全局函數,即這個代碼是所以對象共有的,不需要定義對象才能使用.
static DWORD WINAPI RecvProc(LPVOID lpParameter);
| DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter) { ???? //根據線程函數參數獲取發送端套接字和要設置包含文本控件的窗口句柄 ???????? SOCKET socket = ((RECVPARAM*)lpParameter)->socket; ???????? HWND hWnd = ((RECVPARAM*)lpParameter)->hWnd;//窗口句柄 ???????? SOCKADDR_IN addrRecv; ???????? int len = sizeof(SOCKADDR);?????? ???????? char recvBuf[100]; ???????? char tempBuf[100]; ???????? int recvRst; ???????? CString recvStr; ???????? while(TRUE) ???????? { ???????? //接收數據 ?????????????????? recvRst = recvfrom(socket, recvBuf, 100, 0, (SOCKADDR*)&addrRecv, &len); ?????????????????? if (SOCKET_ERROR == recvRst) ?????????????????? { ??????????????????????????? break; ?????????????????? } ?????????????????? sprintf(tempBuf, "%s說:%s\r\n", inet_ntoa(addrRecv.sin_addr), recvBuf); // ?????????????? recvStr += tempBuf;//這種方式更加簡單不用使用消息 // ?????????????? ::SetDlgItemText(hWnd, IDC_EDIT_RECV, recvStr); ?????????????????? //使用自定義消息的方式來對控件填充內容 ?????????????????? ::PostMessage(hWnd, WM_RECVDATA, 0, (LPARAM)tempBuf); ???????? }?????? ???????? return 0; } |
在OnInitDialog中調用InitSocket完成初始化工作。
| InitSocket();//初始化套接字庫 ???????? RECVPARAM *pRecvParm = new RECVPARAM(); ???????? pRecvParm->socket = m_socket; ???????? pRecvParm->hWnd = m_hWnd; ???????? ???????? HANDLE hThreadRecv = CreateThread(NULL, 0, RecvProc, (LPVOID)pRecvParm, 0, NULL); ???????? CloseHandle(hThreadRecv); |
?
(5)????? ::PostMessage()完成將收到的數據發送給對話框。用自定義的消息參考下面的代碼。注意要將EDitBox的MultiLine屬性選上。
<1>:?????? 在ChatDlg.h中#define WM_RECVDATA?WM_USER+1
<2>:?????? afx_msg void OnRecvData(WPARAM wParam,LPARAM lParam);
<3>:?????? 在ChatDlg.cpp中ON_MESSAGE(WM_RECVDATA,OnRecvData)
??????
| void CChatDlg::OnRecvData(WPARAM wParam,LPARAM lParam) { CString str=(char*)lParam; CString strTemp; GetDlgItemText(IDC_EDIT_RECV,strTemp); str+="\r\n"; str+=strTemp; SetDlgItemText(IDC_EDIT_RECV,str); } |
(6)????? 最后在DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter)
中調用 ::PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);
//不能用SendMessage()
4.對發送按紐的響應代碼:
| void CChatDlg::OnBtnSend() { // TOD Add your control notification handler code here DWORD dwIP; ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); ? SOCKADDR_IN addrTo; addrTo.sin_family=AF_INET; addrTo.sin_port=htons(6000); addrTo.sin_addr.S_un.S_addr=htonl(dwIP); ? CString strSend; GetDlgItemText(IDC_EDIT_SEND,strSend); sendto(m_socket,strSend,strSend.GetLength()+1,0, ?(SOCKADDR*)&addrTo,sizeof(SOCKADDR)); SetDlgItemText(IDC_EDIT_SEND,""); } |
?
總結
以上是生活随笔為你收集整理的VC++学习(15):多线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网页版四则运算(未全部完成)
- 下一篇: python中导入win32com.cl