線程同步與異步套接字編程小結

 

 

事件對象

事件對象也屬於內核對象,包含一個使用計數,一個用於指明該事件是一個自動重置的事件還是一個人工重置的事件的布爾值,另一個用於指明該事件處於已通知狀態還是未通知狀態的布爾值。 兩種不同類型的事件對象。一種是人工重置的事件,另一種是自動重置的事件。當人工重置的事件得到通知時,等待該事件的所有線程均變爲可調度線程。當一個自動重置的事件得到通知時,等待該事件的線程中只有一個線程變爲可調度線程。

The CreateEvent function creates or opens a named or unnamed event object.

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,
  BOOL bManualReset,
  BOOL bInitialState,
  LPCTSTR lpName
);

 

The ResetEvent function sets the specified event object to the nonsignaled state.

BOOL ResetEvent(
  HANDLE hEvent
);

 

The SetEvent function sets the specified event object to the signaled state.

BOOL SetEvent(
  HANDLE hEvent
);

 

事件對象代碼

#include <windows.h>

#include <iostream.h>

 

DWORD WINAPI Fun1Proc(

  LPVOID lpParameter   // thread data

);

 

DWORD WINAPI Fun2Proc(

  LPVOID lpParameter   // thread data

);

 

int tickets=100;

HANDLE g_hEvent;

 

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

 

g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets");// 自動重置

if(g_hEvent)

{

if(ERROR_ALREADY_EXISTS==GetLastError()) // 僅僅運行一個實例

{

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

return;

}

}

SetEvent(g_hEvent); // 設置事件對象爲有信號狀態

 

Sleep(4000);

CloseHandle(g_hEvent);

}

 

DWORD WINAPI Fun1Proc(

  LPVOID lpParameter   // thread data

)

{

while(TRUE)

{

WaitForSingleObject(g_hEvent,INFINITE);

if(tickets>0)

{

Sleep(1);

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

}

else

break;

SetEvent(g_hEvent);

}

 

return 0;

}

 

DWORD WINAPI Fun2Proc(

  LPVOID lpParameter   // thread data

)

{

 

while(TRUE)

{

WaitForSingleObject(g_hEvent,INFINITE);

if(tickets>0)

{

Sleep(1);

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

}

else

break;

SetEvent(g_hEvent);

}

 

return 0;

}

 

 

 

 

 

 

關鍵代碼段 關鍵代碼段(臨界區)工作在用戶方式下。 關鍵代碼段(臨界區)是指一個小代碼段,在代碼能夠執行前,它必須獨佔對某些資源的訪問權

The InitializeCriticalSection function initializes a critical section object.

void InitializeCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

The DeleteCriticalSection function releases all resources used by an unowned critical section object.

void DeleteCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

The EnterCriticalSection function waits for ownership of the specified critical section object. The function returns when the calling thread is granted ownership.

void EnterCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

The LeaveCriticalSection function releases ownership of the specified critical section object.

void LeaveCriticalSection(
  LPCRITICAL_SECTION lpCriticalSection
);

 

臨界區代碼

#include <windows.h>

#include <iostream.h>

 

DWORD WINAPI Fun1Proc(

  LPVOID lpParameter   // thread data

);

 

DWORD WINAPI Fun2Proc(

  LPVOID lpParameter   // thread data

);

 

int tickets=100;

 

CRITICAL_SECTION g_csA;

CRITICAL_SECTION g_csB;

 

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

 

InitializeCriticalSection(&g_csA);

InitializeCriticalSection(&g_csB);

Sleep(4000);

 

DeleteCriticalSection(&g_csA);

DeleteCriticalSection(&g_csB);

}

 

DWORD WINAPI Fun1Proc(

  LPVOID lpParameter   // thread data

)

{

while(TRUE)

{

EnterCriticalSection(&g_csA);

Sleep(1);

EnterCriticalSection(&g_csB); // 死鎖

if(tickets>0)

{

Sleep(1);

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

}

else

break;

LeaveCriticalSection(&g_csB);

LeaveCriticalSection(&g_csA);

}

 

return 0;

}

 

DWORD WINAPI Fun2Proc(

  LPVOID lpParameter   // thread data

)

{

 

while(TRUE)

{

EnterCriticalSection(&g_csB); 

Sleep(1);

EnterCriticalSection(&g_csA); // 死鎖

if(tickets>0)

{

Sleep(1);

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

}

else

break;

LeaveCriticalSection(&g_csA);

LeaveCriticalSection(&g_csB);

}

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

return 0;

}

 

 

 

線程死鎖 哲學家進餐的問題 線程1擁有了臨界區對象A,等待臨界區對象B的擁有權,線程2擁有了臨界區對象B,等待臨界區對象A的擁有權,就造成了死鎖。 互斥對象、事件對象與關鍵代碼段的比較 互斥對象和事件對象屬於內核對象,利用內核對象進行線程同步,速度較慢,但利用互斥對象和事件對象這樣的內核對象,可以在多個進程中的各個線程間進行同步。 關鍵代碼段工作在用戶方式下,同步速度較快,但在使用關鍵代碼段時,很容易進入死鎖狀態,因爲在等待進入關鍵代碼段時無法設定超時值。 線程同步,首選使用關鍵代碼段方式;使用多個臨界區注意死鎖 多個進程多個線程之間通信,可用互斥對象

 

 

基於消息的異步套接字

 

Windows套接字在兩種模式下執行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,執行操作的Winsock函數會一直等待下去,不會立即返回程序(將控制權交還給程序)。而在非阻塞模式下,Winsock函數無論如何都會立即返回。 Windows Sockets爲了支持Windows消息驅動機制,使應用程序開發者能夠方便地處理網絡通信,它對網絡事件採用了基於消息的異步存取策略。 Windows Sockets的異步選擇函數WSAAsyncSelect()提供了消息機制的網絡事件選擇,當使用它登記的網絡事件發生時,Windows應用程序相應的窗口函數將收到一個消息,消息中指示了發生的網絡事件,以及與事件相關的一些信息。

 

The WSAAsyncSelect function requests Windows message-based notification of network events for a socket.

int WSAAsyncSelect(
  SOCKET s,
  HWND hWnd,
  unsigned int wMsg,
  long lEvent
);

 

相關函數說明

int WSAEnumProtocols( LPINT lpiProtocols, LPWSAPROTOCOL_INFO lpProtocolBuffer, ILPDWORD lpdwBufferLength );

Win32平臺支持多種不同的網絡協議,採用Winsock2,就可以編寫可直接使用任何一種協議的網絡應用程序了。通過WSAEnumProtocols函數可以獲得系統中安裝的網絡協議的相關信息

lpiProtocols一個以NULL結尾的協議標識號數組。這個參數是可選的,如果lpiProtocolsNULL,則返回所有可用協議的信息,否則,只返回數組中列出的協議信息。 lpProtocolBuffer[out]一個用WSAPROTOCOL_INFO結構體填充的緩衝區。 WSAPROTOCOL_INFO結構體用來存放或得到一個指定協議的完整信息。 lpdwBufferLength[in, out]在輸入時,指定傳遞給WSAEnumProtocols()函數的lpProtocolBuffer緩衝區的長度;在輸出時,存有獲取所有請求信息需傳遞給WSAEnumProtocols ()函數的最小緩衝區長度。這個函數不能重複調用,傳入的緩衝區必須足夠大以便能存放所有的元素。這個規定降低了該函數的複雜度,並且由於一個 機器上裝載的協議數目往往是很少的,所以並不會產生問題。

SOCKET WSASocket( int af, int type, int protocol, LPWSAPROTOCOL_INFO lpProtocolInfo, GROUP g, DWORD dwFlags );

前三個參數和socket()函數的前三個參數含義一樣。 lpProtocolInfo一個指向WSAPROTOCOL_INFO結構體的指針,該結構定義了所創建的套接字的特性。如果lpProtocolInfoNULL,則WinSock2 DLL使用前三個參數來決定使用哪一個服務提供者,它選擇能夠支持規定的地址族、套接字類型和協議值的第一個傳輸提供者。如果lpProtocolInfo不爲NULL,則套接字綁定到與指定的結構WSAPROTOCOL_INFO相關的提供者。 g保留的。 dwFlags套接字屬性的描述。

int WSARecvFrom( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, struct sockaddr FAR *lpFrom, LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );

s標識套接字的描述符。 lpBuffers[in, out]一個指向WSABUF結構體的指針。每一個WSABUF結構體包含一個緩衝區的指針和緩衝區的長度。 dwBufferCountlpBuffers數組中WSABUF結構體的數目。 lpNumberOfBytesRecvd[out]如果接收操作立即完成,則爲一個指向本次調用所接收的字節數的指針。 lpFlags[in, out]一個指向標誌位的指針。 lpFrom[out]可選指針,指向重疊操作完成後存放源地址的緩衝區。 lpFromlen[in, out]指向from緩衝區大小的指針,僅當指定了lpFrom才需要。 lpOverlapped一個指向WSAOVERLAPPED結構體的指針(對於非重疊套接字則忽略) lpCompletionRoutine一個指向接收操作完成時調用的完成例程的指針(對於非重疊套接字則忽略)

int WSASendTo( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, const struct sockaddr FAR *lpTo, int iToLen, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );

s標識一個套接字(可能已連接)的描述符。 lpBuffers一個指向WSABUF結構體的指針。每一個WSABUF結構體包含一個緩衝區的指針和緩衝區的長度。 dwBufferCountlpBuffers數組中WSABUF結構體的數目。 lpNumberOfBytesSent[out]如果發送操作立即完成,則爲一個指向本次調用所發送的字節數的指針。 dwFlags指示影響操作行爲的標誌位。 lpTo可選指針,指向目標套接字的地址。 iToLenlpTo中地址的長度。 lpOverlapped一個指向WSAOVERLAPPED結構的指針(對於非重疊套接字則忽略) lpCompletionRoutine一個指向接收操作完成時調用的完成例程的指針(對於非重疊套接字則忽略)

 

The WSABUF structure enables the creation or manipulation of a data buffer.

typedef struct __WSABUF {

u_long len;

char FAR* buf;
} WSABUF,

*LPWSABUF;

 

The hostent structure is used by functions to store information about a given host, such as host name, IP address, and so forth. An application should never attempt to modify this structure or to free any of its components. Furthermore, only one copy of the hostent structure is allocated per thread, and an application should therefore copy any information that it needs before issuing any other Windows Sockets API calls.

typedef struct hostent {

char FAR* h_name;

char FAR  FAR** h_aliases;

short h_addrtype;

short h_length;

char FAR  FAR** h_addr_list;
} hostent;

 

 

The gethostbyaddr function retrieves the host information corresponding to a network address.

Note  The gethostbyaddr function has been deprecated by the introduction of the getnameinfo function. Developers creating Windows Sockets 2 applications are urged to use the getnameinfo function instead of the gethostbyaddr function. See Remarks.

struct HOSTENT* FAR gethostbyaddr(
  const char* addr,
  int len,
  int type
);

The gethostbyname function retrieves host information corresponding to a host name from a host database.

Note  The gethostbyname function has been deprecated by the introduction of the getaddrinfo function. Developers creating Windows Sockets 2 applications are urged to use the getaddrinfo function instead of gethostbyname.

struct hostent* FAR gethostbyname(
  const char* name
);

 

 

異步聊天代碼:

 

添加庫文件

 

#include <winsock2.h>

 

BOOL CChatApp::InitInstance()

{

WORD wVersionRequested;

WSADATA wsaData;

int err;

 

wVersionRequested = MAKEWORD( 2, 2 );

 

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ) {

 

return FALSE;

}

 

 

if ( LOBYTE( wsaData.wVersion ) != 2 ||

        HIBYTE( wsaData.wVersion ) != 2 ) {

 

WSACleanup( );

return FALSE;

}

 

…...

}

 

在實例的析構中使用:

WSACleanup();

 

定義消息

#define UM_SOCK                WM_USER+1

 

定義變量

private:

SOCKET m_socket;

 

m_socket=0;

 

if(m_socket)

closesocket(m_socket);

 

初始化套接字

BOOL CChatDlg::InitSocket()

{

m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,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(6000);

if(SOCKET_ERROR==bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR)))

{

MessageBox("綁定失敗!");

return FALSE;

}

if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ))

{

MessageBox("註冊網絡讀取事件失敗!");

return FALSE;

}

 

return TRUE;

}

 

接受數據的消息處理

afx_msg void OnSock(WPARAM,LPARAM);

 

ON_MESSAGE(UM_SOCK,OnSock)

 

void CChatDlg::OnSock(WPARAM wParam,LPARAM lParam)

{

switch(LOWORD(lParam))

{

case FD_READ:

WSABUF wsabuf;

wsabuf.buf=new char[200];

wsabuf.len=200;

DWORD dwRead;

DWORD dwFlag=0;

 

SOCKADDR_IN addrFrom;

int len=sizeof(SOCKADDR);

CString str;

CString strTemp;

HOSTENT *pHost;

if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,

(SOCKADDR*)&addrFrom,&len,NULL,NULL))

{

MessageBox("接收數據失敗!");

return;

}

pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET);

//str.Format("%s說 :%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);

str.Format("%s說 :%s",pHost->h_name,wsabuf.buf);

str+="/r/n";

GetDlgItemText(IDC_EDIT_RECV,strTemp);

str+=strTemp;

SetDlgItemText(IDC_EDIT_RECV,str);

break;

}

}

 

發送數據

void CChatDlg::OnBtnSend()

{

// TODO: Add your control notification handler code here

DWORD dwIP;

CString strSend;

WSABUF wsabuf;

DWORD dwSend;

int len;

CString strHostName;

SOCKADDR_IN addrTo;

HOSTENT* pHost;

if(GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="")

{

((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);

addrTo.sin_addr.S_un.S_addr=htonl(dwIP);

}

else

{

pHost=gethostbyname(strHostName);

addrTo.sin_addr.S_un.S_addr=*((DWORD*)pHost->h_addr_list[0]);

}

 

addrTo.sin_family=AF_INET;

addrTo.sin_port=htons(6000);

 

GetDlgItemText(IDC_EDIT_SEND,strSend);

len=strSend.GetLength();

wsabuf.buf=strSend.GetBuffer(len);

wsabuf.len=len+1;

 

SetDlgItemText(IDC_EDIT_SEND,"");

 

if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0,

(SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))

{

MessageBox("發送數據失敗!");

return;

}

 

}

 

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