多線程編程小結

程序和進程

程序是計算機指令的集合,它以文件的形式存儲在磁盤上。 進程:通常被定義爲一個正在運行的程序的實例,是一個程序在其自身的地址空間中的一次執行活動。 進程是資源申請、調度和獨立運行的單位,因此,它使用系統中的運行資源;而程序不能申請系統資源,不能被系統調度,也不能作爲獨立運行的單位,因此,它不佔用系統的運行資源。 進程由兩個部分組成:

1、操作系統用來管理進程的內核對象。內核對象也是系統用來存放關於進程的統計信息的地方。

2、地址空間。它包含所有可執行模塊或DLL模塊的代碼和數據。它還包含動態內存分配的空間。如線程堆棧和堆分配空間。

 

進程

進程是不活潑的。進程從來不執行任何東西,它只是線程的容器。若要使進程完成某項操作,它必須擁有一個在它的環境中運行的線程,此線程負責執行包含在進程的地址空間中的代碼。 單個進程可能包含若干個線程,這些線程都同時 執行進程地址空間中的代碼。 每個進程至少擁有一個線程,來執行進程的地址空間中的代碼。當創建一個進程時,操作系統會自動創建這個進程的第一個線程,稱爲主線程。此後,該線程可以創建其他的線程。

 

進程地址空間

系統賦予每個進程獨立的虛擬地址空間。對於32位進程來說,這個地址空間是4GB 每個進程有它自己的私有地址空間。進程A可能有一個存放在它的地址空間中的數據結構,地址是0x12345678,而進程B則有一個完全不同的數據結構存放在它的地址空間中,地址是0x12345678。當進程A中運行的線程訪問地址爲0x12345678的內存時,這些線程訪問的是進程A的數據結構。當進程B中運行的線程訪問地址爲0x12345678的內存時,這些線程訪問的是進程B的數據結構。進程A中運行的線程不能訪問進程B的地址空間中的數據結構,反之亦然。 4GB是虛擬的地址空間,只是內存地址的一個範圍。在你能成功地訪問數據而不會出現非法訪問之前,必須賦予物理存儲器,或者將物理存儲器映射到各個部分的地址空間。 4GB虛擬地址空間中,2GB是內核方式分區,供內核代碼、設備驅動程序、設備I/O高速緩衝、非頁面內存池的分配和進程頁面表等使用,而用戶方式分區使用的地址空間約爲2GB,這個分區是進程的私有地址空間所在的地方。一個進程不能讀取、寫入、或者以任何方式訪問駐留在該分區中的另一個進程的數據。對於所有應用程序來說,該分區是維護進程的大部分數據的地方。

線程

線程由兩個部分組成:

1、線程的內核對象,操作系統用它來對線程實施管理。內核對象也是系統用來存放線程統計信息的地方。

2、線程堆棧,它用於維護線程在執行代碼時需要的所有參數和局部變量。

當創建線程時,系統創建一個線程內核對象。該線程內核對象不是線程本身,而是操作系統用來管理線程的較小的數據結構。可以將線程內核對象視爲由關於線程的統計信息組成的一個小型數據結構。 線程總是在某個進程環境中創建。系統從進程的地址空間中分配內存,供線程的堆棧使用。新線程運行的進程環境與創建線程的環境相同。因此,新線程可以訪問進程的內核對象的所有句柄、進程中的所有內存和在這個相同的進程中的所有其他線程的堆棧。這使得單個進程中的多個線程確實能夠非常容易地互相通信。 線程只有一個內核對象和一個堆棧,保留的記錄很少,因此所需要的內存也很少 因爲線程需要的開銷比進程少,因此在編程中經常採用多線程來解決編程問題,而儘量避免創建新的進程。

線程運行

操作系統爲每一個運行線程安排一定的CPU時間 —— 時間片。系統通過一種循環的方式爲線程提供時間片,線程在自己的時間內運行,因時間片相當短,因此,給用戶的感覺,就好像線程是同時運行的一樣。 如果計算機擁有多個CPU,線程就能真正意義上同時運行了。

互斥對象

互斥對象(mutex)屬於內核對象,它能夠確保線程擁有對單個資源的互斥訪問權 互斥對象包含一個使用數量,一個線程ID一個計數器 ID用於標識系統中的哪個線程當前擁有互斥對象,計數器用於指明該線程擁有互斥對象的次數。 如果操作系統判斷線程中止,將將互斥對象的ID和計數器歸零,其他線程又可使用該互斥對象。通過返回值可以判斷

 

HANDLE CreateThread(
 
LPSECURITY_ATTRIBUTES lpThreadAttributes,
  SIZE_T dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID lpParameter,
  DWORD dwCreationFlags,
  LPDWORD lpThreadId
);

 

The CreateMutex function creates or opens a named or unnamed mutex object.

HANDLE CreateMutex(
 
LPSECURITY_ATTRIBUTES lpMutexAttributes,
  BOOL bInitialOwner,
  LPCTSTR lpName
);

The WaitForSingleObject function returns when the specified object is in the signaled state or the time-out interval elapses.

To enter an alertable wait state, use the WaitForSingleObjectEx function. To wait for multiple objects, use the WaitForMultipleObjects.

DWORD WaitForSingleObject(
 
HANDLE hHandle,
  DWORD dwMilliseconds
);

 

 

多線程代碼:

#include <windows.h>

#include <iostream.h>

 

// 申明

DWORD WINAPI Fun1Proc(

  LPVOID lpParameter   // thread data

);

 

DWORD WINAPI Fun2Proc(

  LPVOID lpParameter   // thread data

);

 

int index=0;

int tickets=100;

HANDLE hMutex; // 必須誰擁有誰釋放,請求幾次必須釋放幾次

void main()

{

HANDLE hThread1;

HANDLE hThread2;

hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);

hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);

CloseHandle(hThread1); // 關閉句柄,並沒有終止新創建的線程,只表示在主線程中對新線程的使用不感興趣,系統遞減線程引用計數。線程的銷燬是在主線程退出時處理。

CloseHandle(hThread2);

/*while(index++<1000)

cout<<"main thread is running"<<endl;*/

//hMutex=CreateMutex(NULL,TRUE,NULL);

hMutex=CreateMutex(NULL,TRUE,"tickets"); // 創建一個命名的互斥對象

if(hMutex)

{

if(ERROR_ALREADY_EXISTS==GetLastError())// 該名字的互斥對象已經存在,即已有一個實例在運行。

{

cout<<"only instance can run!"<<endl;

return;

}

}

WaitForSingleObject(hMutex,INFINITE);

ReleaseMutex(hMutex);

ReleaseMutex(hMutex);

Sleep(4000);

//        Sleep(10);

}

 

DWORD WINAPI Fun1Proc(

  LPVOID lpParameter   // thread data

)

{

/*while(index++<1000)

cout<<"thread1 is running"<<endl;*/

 

/*while(TRUE)

{

//ReleaseMutex(hMutex);

WaitForSingleObject(hMutex,INFINITE);

if(tickets>0)

{

Sleep(1);

cout<<"thread1 sell ticket : "<<tickets--<<endl;

}

else

break;

ReleaseMutex(hMutex);

}*/

 

WaitForSingleObject(hMutex,INFINITE);

cout<<"thread1 is running"<<endl;

return 0;

}

 

DWORD WINAPI Fun2Proc(

  LPVOID lpParameter   // thread data

)

{

 

/*while(TRUE)

{

//ReleaseMutex(hMutex);

WaitForSingleObject(hMutex,INFINITE);

if(tickets>0)

{

Sleep(1);

cout<<"thread2 sell ticket : "<<tickets--<<endl;

}

else

break;

ReleaseMutex(hMutex);

}*/

WaitForSingleObject(hMutex,INFINITE);

cout<<"thread2 is running"<<endl;

return 0;

}

 

MFC中使用套接字

Call this function in your CWinApp::InitInstance override to initialize Windows Sockets.

BOOL AfxSocketInit(
   WSADATA* lpwsaData = NULL
);

Afxsock.h

 

private:

SOCKET 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_family=AF_INET;

addrSock.sin_port=htons(6000);

addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

 

int retval;

retval=bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));

if(SOCKET_ERROR==retval)

{

closesocket(m_socket);

MessageBox("綁定失敗!");

return FALSE;

}

return TRUE;

}

 

爲了防止接受阻塞,需要使用多線程,在初始化socket後,創建線程

#define         WM_RECVDATA                WM_USER+1

struct RECVPARAM

{

SOCKET sock;

HWND hwnd;

};

 

RECVPARAM *pRecvParam=new RECVPARAM;

pRecvParam->sock=m_socket;

pRecvParam->hwnd=m_hWnd;

HANDLE hThread=CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);

CloseHandle(hThread);


線程函數,不使用全局函數,則使用靜態函數申明:

static DWORD WINAPI RecvProc(LPVOID lpParameter);

 

// 接受數據線程代碼:

DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter)

{

SOCKET sock=((RECVPARAM*)lpParameter)->sock;

HWND hwnd=((RECVPARAM*)lpParameter)->hwnd;

delete lpParameter;        //釋放內存的操作。

 

SOCKADDR_IN addrFrom;

int len=sizeof(SOCKADDR);

 

char recvBuf[200];

char tempBuf[300];

int retval;

 

while(TRUE) // 死循環

{

retval=recvfrom(sock,recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);

if(SOCKET_ERROR==retval)

break;

sprintf(tempBuf,"%s說: %s",inet_ntoa(addrFrom.sin_addr),recvBuf);

::PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);

}

return 0;

}

 

// 接受數據的消息處理

afx_msg void OnRecvData(WPARAM wParam,LPARAM lParam);

 

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

}

 

// 發送數據端代碼

void CChatDlg::OnBtnSend()

{

// TODO: Add your control notification handler code here

DWORD dwIP;

((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);// 取得IP地址

 

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,""); // 清空編輯框

}

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章