孙鑫16课线程同步与异步套接字编程.docx
- 文档编号:5419234
- 上传时间:2022-12-16
- 格式:DOCX
- 页数:14
- 大小:19.20KB
孙鑫16课线程同步与异步套接字编程.docx
《孙鑫16课线程同步与异步套接字编程.docx》由会员分享,可在线阅读,更多相关《孙鑫16课线程同步与异步套接字编程.docx(14页珍藏版)》请在冰豆网上搜索。
孙鑫16课线程同步与异步套接字编程
孙鑫16课:
线程同步与异步套接字编程
利用事件对象实现线程同步:
事件对象(互斥对象也属于内核对象)也属于内核对象,包含一个使用计数,一个用于指明该事件是一个自动重置的事件还是一个人工重置的事件的布尔值,另一个用于指明该事件处于已通知状态还是未通知状态的布尔值。
有两种不同类型的事件对象。
一种是人工重置的事件,另一种是自动重置的事件。
当人工重置的事件得到通知时,等待该事件的所有线程均变为可调度线程。
当一个自动重置的事件得到通知时,等待该事件的线程中只有一个线程变为可调度线程。
建立一个WIN32的控制台应用程序:
#include
#include
DWORDWINAPIFun1Proc(
LPVOIDlpParameter//threaddata
);
DWORDWINAPIFun2Proc(
LPVOIDlpParameter//threaddata
);
inttickets=100;
HANDLEg_hEvent;
voidmain()
{
HANDLEhThread1;
HANDLEhThread2;
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,NULL);
g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets");//自动重置,初始无信号。
if(g_hEvent)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"onlyinstancecanrun!
"< return; } } SetEvent(g_hEvent); Sleep(4000); CloseHandle(g_hEvent); } DWORDWINAPIFun1Proc( LPVOIDlpParameter//threaddata ) { while(TRUE) { WaitForSingleObject(g_hEvent,INFINITE); //ResetEvent(g_hEvent); if(tickets>0) { Sleep (1); cout<<"thread1sellticket: "< } else break; SetEvent(g_hEvent); } return0; } DWORDWINAPIFun2Proc( LPVOIDlpParameter//threaddata ) { while(TRUE) { WaitForSingleObject(g_hEvent,INFINITE); //ResetEvent(g_hEvent); if(tickets>0) { Sleep (1); cout<<"thread2sellticket: "< } else break; SetEvent(g_hEvent); } return0; } 下面创建另一个线程同步的方式: 关键代码段: 关键代码段(临界区)工作在用户方式下。 关键代码段(临界区)是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。 可以把访问同一种资源的代码看成是关键代码段。 新建一个WIN32的控制台程序: #include #include DWORDWINAPIFun1Proc( LPVOIDlpParameter//threaddata ); DWORDWINAPIFun2Proc( LPVOIDlpParameter//threaddata ); inttickets=100; CRITICAL_SECTIONCriticalSection; voidmain() { HANDLEhThread1; HANDLEhThread2; hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); CloseHandle(hThread1); CloseHandle(hThread2); InitializeCriticalSection(&CriticalSection); Sleep(4000); DeleteCriticalSection(&CriticalSection); } DWORDWINAPIFun1Proc( LPVOIDlpParameter//threaddata ) { while(TRUE) { EnterCriticalSection(&CriticalSection);//在要保护的资源代码前加上这句, //在访问后释放临界区对象所有权。 如果得不到对临界资源的访问权,则线程等待下去。 //线程1执行完成之后,线程1退出,但是如果线程1没有释放临界区对象的所有权,则线程2一直等待临界区对象的使用权, //则线程2无法得到临界区对象的使用权,线程2一直等,直到主线程退出。 进程退出,线程2也退出。 if(tickets>0) { Sleep (1); cout<<"thread1sellticket: "< } else break; LeaveCriticalSection(&CriticalSection); } return0; } DWORDWINAPIFun2Proc( LPVOIDlpParameter//threaddata ) { while(TRUE) { EnterCriticalSection(&CriticalSection); if(tickets>0) { Sleep (1); cout<<"thread2sellticket: "< } else break; LeaveCriticalSection(&CriticalSection); } return0; } 在使用临界区对象的时候,要注意释放临界区对象的所有权。 要注意死锁的问题。 死锁问题: 线程1拥有了临界区对象A,等待临界区对象B的拥有权,线程2拥有了临界区对象B,等待临界区对象A的拥有权,就造成了死锁。 死锁代码如下: #include #include DWORDWINAPIFun1Proc( LPVOIDlpParameter//threaddata ); DWORDWINAPIFun2Proc( LPVOIDlpParameter//threaddata ); inttickets=100; CRITICAL_SECTIONg_csA; CRITICAL_SECTIONg_csB; voidmain() { HANDLEhThread1; HANDLEhThread2; 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); } DWORDWINAPIFun1Proc( LPVOIDlpParameter//threaddata ) { while(TRUE) { EnterCriticalSection(&g_csA); Sleep (1); EnterCriticalSection(&g_csB); if(tickets>0) { Sleep (1); cout<<"thread1sellticket: "< } else break; LeaveCriticalSection(&g_csB); LeaveCriticalSection(&g_csA); } return0; } DWORDWINAPIFun2Proc( LPVOIDlpParameter//threaddata ) { while(TRUE) { EnterCriticalSection(&g_csB); Sleep (1); EnterCriticalSection(&g_csA); if(tickets>0) { Sleep (1); cout<<"thread2sellticket: "< } else break; LeaveCriticalSection(&g_csA);//释放的顺序无所谓 LeaveCriticalSection(&g_csB); } cout<<"thread2isrunning! "< return0; } 三种实现线程同步的方式的比较: 互斥对象、事件对象与关键代码段的比较 ⏹互斥对象和事件对象属于内核对象,利用内核对象进行线程同步,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的各个线程间进行同步。 ⏹关键代码段是工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。 推荐书目 《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(); returnFALSE; } 在stdafx.h中加入: #include 再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("创建套接字失败! "); returnFALSE; } 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))) { MessageBox("绑定失败! "); returnFALSE; } if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ))//一旦有FD_READ事件发生,系统就会触发这个事件, //系统就会通过UM_SOCK消息通知我们,在这个消息的响应函数中,接收数据就能收到数据。 { MessageBox("注册网络读取事件失败! "); returnFALSE; } 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("接收数据失败! "); 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; } } voidCChatDlg: : 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,&wsabuf,1,&dwSend,0, (SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL)) { MessageBox("发送数据失败! "); return; } }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 孙鑫 16 线程 同步 异步 套接 编程