孙鑫16课线程同步与异步套接字编程Word格式.docx
- 文档编号:18406748
- 上传时间:2022-12-16
- 格式:DOCX
- 页数:14
- 大小:19.20KB
孙鑫16课线程同步与异步套接字编程Word格式.docx
《孙鑫16课线程同步与异步套接字编程Word格式.docx》由会员分享,可在线阅读,更多相关《孙鑫16课线程同步与异步套接字编程Word格式.docx(14页珍藏版)》请在冰豆网上搜索。
//g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
g_hEvent=CreateEvent(NULL,FALSE,FALSE,"
tickets"
//自动重置,初始无信号。
if(g_hEvent)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<
<
"
onlyinstancecanrun!
endl;
return;
}
}
SetEvent(g_hEvent);
Sleep(4000);
CloseHandle(g_hEvent);
}
)
while(TRUE)
WaitForSingleObject(g_hEvent,INFINITE);
//ResetEvent(g_hEvent);
if(tickets>
0)
Sleep
(1);
thread1sellticket:
"
tickets--<
else
break;
SetEvent(g_hEvent);
return0;
thread2sellticket:
下面创建另一个线程同步的方式:
关键代码段:
关键代码段(临界区)工作在用户方式下。
关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。
可以把访问同一种资源的代码看成是关键代码段。
新建一个WIN32的控制台程序:
LPVOIDlpParameter//threaddata
);
CRITICAL_SECTIONCriticalSection;
InitializeCriticalSection(&
CriticalSection);
DeleteCriticalSection(&
)
EnterCriticalSection(&
//在要保护的资源代码前加上这句,
//在访问后释放临界区对象所有权。
如果得不到对临界资源的访问权,则线程等待下去。
//线程1执行完成之后,线程1退出,但是如果线程1没有释放临界区对象的所有权,则线程2一直等待临界区对象的使用权,
//则线程2无法得到临界区对象的使用权,线程2一直等,直到主线程退出。
进程退出,线程2也退出。
LeaveCriticalSection(&
在使用临界区对象的时候,要注意释放临界区对象的所有权。
要注意死锁的问题。
死锁问题:
线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程2拥有了临界区对象B,等待临界区对象A的拥有权,就造成了死锁。
死锁代码如下:
CRITICAL_SECTIONg_csA;
CRITICAL_SECTIONg_csB;
g_csA);
g_csB);
Sleep
(1);
//释放的顺序无所谓
cout<
thread2isrunning!
三种实现线程同步的方式的比较:
互斥对象、事件对象与关键代码段的比较
⏹互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。
⏹关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。
推荐书目
《Windows核心编程》机械工业出版社
在实现线程同步时,首选关键代码段。
若在MFC程序中,可以在一个类的构造函数中调用InitializeCriticalSection();
在析构函数中调用DeleteCriticalSection()。
在要保护的代码前面加上EnterCriticalSection();
在访问完要保护的资源后调用LeaveCriticalSection();
记得一定要释放关键代码段。
如果构造了多个临界区对象,要注意线程死锁。
多个进程的各个线程间,要用互斥对象和事件对象。
基于消息的异步套接字编程:
⏹Windows套接字在两种模式下执行I/O操作,阻塞和非阻塞。
在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回程序(将控制权交还给程序)。
而在非阻塞模式下,Winsock函数无论如何都会立即返回。
⏹WindowsSockets为了支持Windows消息驱动机制,使应用程序开发者能够方便地处理网络通信,它对网络事件采用了基于消息的异步存取策略。
⏹WindowsSockets的异步选择函数WSAAsyncSelect()提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。
intWSAEnumProtocols(LPINTlpiProtocols,LPWSAPROTOCOL_INFOlpProtocolBuffer,ILPDWORDlpdwBufferLength);
⏹Win32平台支持多种不同的网络协议,采用Winsock2,就可以编写可直接使用任何一种协议的网络应用程序了。
通过WSAEnumProtocols函数可以获得系统中安装的网络协议的相关信息。
⏹lpiProtocols,一个以NULL结尾的协议标识号数组。
这个参数是可选的,如果lpiProtocols为NULL,则返回所有可用协议的信息,否则,只返回数组中列出的协议信息。
⏹lpProtocolBuffer,[out],一个用WSAPROTOCOL_INFO结构体填充的缓冲区。
WSAPROTOCOL_INFO结构体用来存放或得到一个指定协议的完整信息。
⏹lpdwBufferLength,[in,out],在输入时,指定传递给WSAEnumProtocols()函数的lpProtocolBuffer缓冲区的长度;
在输出时,存有获取所有请求信息需传递给WSAEnumProtocols()函数的最小缓冲区长度。
这个函数不能重复调用,传入的缓冲区必须足够大以便能存放所有的元素。
这个规定降低了该函数的复杂度,并且由于一个机器上装载的协议数目往往是很少的,所以并不会产生问题。
下面采用异步套接字编写一个网络聊天室程序:
新建一个基于对话框的程序:
在BOOLCChatApp:
:
InitInstance()中加入:
WORDwVersionRequested;
WSADATAwsaData;
interr;
wVersionRequested=MAKEWORD(2,2);
//最高版本
err=WSAStartup(wVersionRequested,&
wsaData);
if(err!
=0)
returnFALSE;
if(LOBYTE(wsaData.wVersion)!
=2||
HIBYTE(wsaData.wVersion)!
=2)
WSACleanup();
在stdafx.h中加入:
WINSOCK2.H>
再LINKWs2_32.lib
给CChatApp增加一个析构函数,在此函数中终止对套接字库的使用:
CChatApp:
~CChatApp()
WSACleanup();
为CChatDlg增加private:
SOCKETm_socket;
在CChatDlg:
CChatDlg中对套接字进行初始化。
m_socket=0;
在析构函数中关闭套接字:
CChatDlg:
~CChatDlg()
if(m_socket)
closesocket(m_socket);
}
在CChatDlg中增加BOOLCChatDlg:
InitSocket()函数。
函数代码如下:
BOOLCChatDlg:
InitSocket()
m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);
if(INVALID_SOCKET==m_socket)
MessageBox("
创建套接字失败!
SOCKADDR_INskaddr;
skaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
skaddr.sin_family=AF_INET;
skaddr.sin_port=htons(6000);
if(SOCKET_ERROR==bind(m_socket,(SOCKADDR*)&
skaddr,sizeof(SOCKADDR_IN)))
绑定失败!
if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ))//一旦有FD_READ事件发生,系统就会触发这个事件,
//系统就会通过UM_SOCK消息通知我们,在这个消息的响应函数中,接收数据就能收到数据。
注册网络读取事件失败!
returnTRUE;
在BOOLCChatDlg:
OnInitDialog()中加入
InitSocket();
在ChatDlg.h加入:
#defineUM_SOCKWM_USER+1
afx_msgvoidOnSock(WPARAMwParam,LPARAMlParam);
在BEGIN_MESSAGE_MAP(CChatDlg,CDialog)中加上红色的那句
//{{AFX_MSG_MAP(CChatDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_COMMAND(UM_SOCK,OnSock)
END_MESSAGE_MAP()
将接收编辑框的多行属性选上。
voidCChatDlg:
OnSock(WPARAMwParam,LPARAMlParam)
switch(LOWORD(lParam))
caseFD_READ:
WSABUFwsabuf;
wsabuf.buf=newchar[200];
wsabuf.len=200;
DWORDdwRead;
DWORDdwFlag=0;
SOCKADDR_INaddrFrom;
intlen=sizeof(SOCKADDR);
CStringstr;
CStringstrTemp;
HOSTENT*pHost;
if(SOCKET_ERROR==WSARecvFrom(m_socket,&
wsabuf,1,&
dwRead,&
dwFlag,
(SOCKADDR*)&
addrFrom,&
len,NULL,NULL))
MessageBox("
接收数据失败!
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("
pHost->
h_name,wsabuf.buf);
str+="
\r\n"
;
GetDlgItemText(IDC_EDIT_RECV,strTemp);
str+=strTemp;
SetDlgItemText(IDC_EDIT_RECV,str);
break;
OnBtnSend()
//TODO:
Addyourcontrolnotificationhandlercodehere
DWORDdwIP;
CStringstrSend;
WSABUFwsabuf;
DWORDdwSend;
intlen;
CStringstrHostName;
SOCKADDR_INaddrTo;
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,&
dwSend,0,
(SOCKADDR*)&
addrTo,sizeof(SOCKADDR),NULL,NULL))
发送数据失败!
return;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 孙鑫 16 线程 同步 异步 套接 编程