MFC - 线程同步与异步套接字 (孙鑫C++第十六讲笔记整理)

1.事件对象:来实现线程的同步。与互斥对象一样均属于内核对象。

 当人工重置有信号时,所有线程均得到信号,所以不能设为人工重置。代码就不贴了,通过创建匿名的事件对象,也可以让一个程序只能运行一个实例。

 

 

2.关键代码段实现线程的同步:类似公用电话亭,只有当电话亭里面没人了,其它人才可以再进去打电话。用了4个函数,这种方法比较简单!但缺点是如果使用了多少关键代码码,容易赞成线程的死锁

 

 

3.线程死锁,用关键代码示例,用了两个临界区对象,实战中要注意避免这种错误!

 

 

4.使用异步套接字编写网络聊天室

 1)加载套接字库,进行版本协商,包含头文件,链接库文件,这次请示的是2.2版本!

 2)在类CChatDlg中增加一个成员变量m_socket,在析构函数中释放这个变量

 3)利用WSASocket()创建套接字(数据报类型的UDP型的)

 4)然后调用WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ)为网络事件定义消息!此时如果发生FD_READ消息,系统会发送UM_SOCK消息给应用程序!程序并不会阻塞在这儿了!

 以上是在BOOL CChatDlg::OnInitDialog()完成

 5)然后完成消息响应!

 头文件中:#define UM_SOCK WM_USER+1

 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;

 }

}

OK!

 6)完成数据发送的功能!

     void CChatDlg::OnBtnSend()

{

 // TOD 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;

 }

 

}     7)完成将主机名转换为IP地址的功能,以前将IP地址转换为主机名的功能,单线程的聊天室创建完毕!性能并且非常出色!

 

下面是一些具体的代码:

[cpp]  view plain copy
  1. #include<windows.h>  
  2. #include<iostream.h>  
  3.   
  4.   
  5. DWORD WINAPI ThreadProc1(  
  6.                          LPVOID lpParameter   
  7.                          );  
  8. DWORD WINAPI ThreadProc2(  
  9.                          LPVOID lpParameter    
  10.                          );  
  11.   
  12.   
  13.   
  14. int tickes=100;  
  15. HANDLE hEvent;  
  16. int main()  
  17. {  
  18.     HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);  
  19.     HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);  
  20.       
  21.     hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);//自动,有信号  
  22.       
  23.     CloseHandle(hThread1);  
  24.     CloseHandle(hThread2);  
  25.     Sleep(4000);  
  26.       
  27.     return 0;  
  28.       
  29. }  
  30.   
  31. DWORD WINAPI ThreadProc1(  
  32.                          LPVOID lpParameter   // thread data  
  33.                          )  
  34. {  
  35.       
  36.     while(TRUE)  
  37.     {  
  38.         SetEvent(hEvent);  
  39.         WaitForSingleObject(hEvent,INFINITE);  
  40.         if(tickes>0)  
  41.         {  
  42.             cout<<"thread1 sell ticke: "<<tickes--<<endl;  
  43.         }  
  44.         else  
  45.         {  
  46.             break;  
  47.         }  
  48.         //ReleaseMutex(hEvent);  
  49.         ResetEvent(hEvent);  
  50.     }  
  51.     return 0;  
  52.       
  53. }  
  54.   
  55.   
  56. DWORD WINAPI ThreadProc2(  
  57.                          LPVOID lpParameter   // thread data  
  58.                          )  
  59. {  
  60.     while(TRUE)  
  61.     {  
  62.         SetEvent(hEvent);  
  63.         WaitForSingleObject(hEvent,INFINITE);  
  64.         if(tickes>0)  
  65.         {  
  66.             cout<<"thread2 sell ticke: "<<tickes--<<endl;  
  67.         }  
  68.         else  
  69.         {  
  70.             break;  
  71.         }  
  72.         //ReleaseMutex(hEvent);  
  73.         ResetEvent(hEvent);  
  74.     }  
  75.     return 0;  
  76. }  


 

[cpp]  view plain copy
  1. //CreateEvent设置自定的,并且初始有信号  
  2. /*#include<windows.h> 
  3. #include<iostream.h> 
  4.  
  5.  
  6. DWORD WINAPI ThreadProc1( 
  7.                          LPVOID lpParameter  
  8.                          ); 
  9. DWORD WINAPI ThreadProc2( 
  10.                          LPVOID lpParameter   
  11.                          ); 
  12.  
  13.  
  14.  
  15. int tickes=100; 
  16. HANDLE hEvent; 
  17. int main() 
  18. { 
  19.     HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL); 
  20.     HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL); 
  21.      
  22.     hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);//自动,有信号 
  23.      
  24.     CloseHandle(hThread1); 
  25.     CloseHandle(hThread2); 
  26.     Sleep(4000); 
  27.      
  28.     return 0; 
  29.      
  30. } 
  31.  
  32. DWORD WINAPI ThreadProc1( 
  33.                          LPVOID lpParameter   // thread data 
  34.                          ) 
  35. { 
  36.      
  37.     while(TRUE) 
  38.     { 
  39.         WaitForSingleObject(hEvent,INFINITE); 
  40.         if(tickes>0) 
  41.         { 
  42.             cout<<"thread1 sell ticke: "<<tickes--<<endl; 
  43.         } 
  44.         else 
  45.         { 
  46.             break; 
  47.         } 
  48.      
  49.     } 
  50.     return 0; 
  51.      
  52. } 
  53.  
  54.  
  55. DWORD WINAPI ThreadProc2( 
  56.                          LPVOID lpParameter   // thread data 
  57.                          ) 
  58. { 
  59.     while(TRUE) 
  60.     { 
  61.         WaitForSingleObject(hEvent,INFINITE); 
  62.         if(tickes>0) 
  63.         { 
  64.             cout<<"thread2 sell ticke: "<<tickes--<<endl; 
  65.         } 
  66.         else 
  67.         { 
  68.             break; 
  69.         } 
  70.      
  71.     } 
  72.     return 0; 
  73. }*/  
  74. //结论是,设置自动的,并且开始有信号(无信号可以调用SetEvent来使无信号到有信号的一个转变)  
  75. //只有一个线程获得了信号,并且运行完后(时间片到了),则下一个线程是无法运行的,因为此时只有一个  
  76. //线程有信号,并且运行完后,马上使得事件对象无信号  


 

[cpp]  view plain copy
  1. //CreateEvent手动,一开始就无信号  
  2. /*#include<windows.h> 
  3. #include<iostream.h> 
  4.  
  5.  
  6. DWORD WINAPI ThreadProc1( 
  7.                          LPVOID lpParameter  
  8.                          ); 
  9. DWORD WINAPI ThreadProc2( 
  10.                          LPVOID lpParameter   
  11.                          ); 
  12.  
  13.  
  14.  
  15. int tickes=100; 
  16. HANDLE hEvent; 
  17. int main() 
  18. { 
  19.     HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL); 
  20.     HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL); 
  21.      
  22.     hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//手动,无信号 
  23.     SetEvent(hEvent); 
  24.     CloseHandle(hThread1); 
  25.     CloseHandle(hThread2); 
  26.     Sleep(4000); 
  27.      
  28.     return 0; 
  29.      
  30. } 
  31.  
  32. DWORD WINAPI ThreadProc1( 
  33.                          LPVOID lpParameter   // thread data 
  34.                          ) 
  35. { 
  36.      
  37.     while(TRUE) 
  38.     { 
  39.         WaitForSingleObject(hEvent,INFINITE); 
  40.         if(tickes>0) 
  41.         { 
  42.             cout<<"thread1 sell ticke: "<<tickes--<<endl; 
  43.         } 
  44.         else 
  45.         { 
  46.             break; 
  47.         } 
  48.          
  49.     } 
  50.     return 0; 
  51.      
  52. } 
  53.  
  54.  
  55. DWORD WINAPI ThreadProc2( 
  56.                          LPVOID lpParameter   // thread data 
  57.                          ) 
  58. { 
  59.     while(TRUE) 
  60.     { 
  61.         WaitForSingleObject(hEvent,INFINITE); 
  62.         if(tickes>0) 
  63.         { 
  64.             cout<<"thread2 sell ticke: "<<tickes--<<endl; 
  65.         } 
  66.         else 
  67.         { 
  68.             break; 
  69.         } 
  70.          
  71.     } 
  72.     return 0; 
  73. }*/  
  74. //使用手动设置,则任何线程都获得了信号,都可以运行,可以使用ResetEvent使得信号变得无效  


 

[cpp]  view plain copy
  1. //关键代码段,临界区域  
  2. //如果只是Enter了但是没有Leave则下一个线程获取不了信号,下一个线程得不到执行  
  3. /* 
  4. #include<windows.h> 
  5. #include<iostream.h> 
  6.  
  7.  
  8. DWORD WINAPI ThreadProc1( 
  9.                          LPVOID lpParameter  
  10.                          ); 
  11. DWORD WINAPI ThreadProc2( 
  12.                          LPVOID lpParameter   
  13.                          ); 
  14.  
  15.  
  16.  
  17. int tickes=100; 
  18. CRITICAL_SECTION section; 
  19. int main() 
  20. { 
  21.     HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL); 
  22.     HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL); 
  23.      
  24.      
  25.      
  26.     CloseHandle(hThread1); 
  27.     CloseHandle(hThread2); 
  28.  
  29.     InitializeCriticalSection(§ion); 
  30.     Sleep(4000); 
  31.      
  32.     DeleteCriticalSection(§ion); 
  33.     return 0; 
  34.      
  35. } 
  36.  
  37. DWORD WINAPI ThreadProc1( 
  38.                          LPVOID lpParameter   // thread data 
  39.                          ) 
  40. { 
  41.      
  42.     while(TRUE) 
  43.     { 
  44.         EnterCriticalSection(§ion); 
  45.         if(tickes>0) 
  46.         { 
  47.             cout<<"thread1 sell ticke: "<<tickes--<<endl; 
  48.         } 
  49.         else 
  50.         { 
  51.             break; 
  52.         } 
  53.         LeaveCriticalSection(§ion); 
  54.          
  55.     } 
  56.     return 0; 
  57.      
  58. } 
  59.  
  60.  
  61. DWORD WINAPI ThreadProc2( 
  62.                          LPVOID lpParameter   // thread data 
  63.                          ) 
  64. { 
  65.     while(TRUE) 
  66.     { 
  67.         EnterCriticalSection(§ion); 
  68.         if(tickes>0) 
  69.         { 
  70.             cout<<"thread2 sell ticke: "<<tickes--<<endl; 
  71.         } 
  72.         else 
  73.         { 
  74.             break; 
  75.         } 
  76.         LeaveCriticalSection(§ion); 
  77.     } 
  78.     return 0; 
  79. }*/  


 

[cpp]  view plain copy
  1. //死锁的体现  
  2.   
  3. /*#include<windows.h> 
  4. #include<iostream.h> 
  5.  
  6.  
  7. DWORD WINAPI ThreadProc1( 
  8.                          LPVOID lpParameter  
  9.                          ); 
  10. DWORD WINAPI ThreadProc2( 
  11.                          LPVOID lpParameter   
  12.                          ); 
  13.  
  14.  
  15.  
  16. int tickes=100; 
  17. CRITICAL_SECTION sectionA; 
  18. CRITICAL_SECTION sectionB; 
  19. int main() 
  20. { 
  21.     HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL); 
  22.     HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL); 
  23.      
  24.      
  25.      
  26.     CloseHandle(hThread1); 
  27.     CloseHandle(hThread2); 
  28.      
  29.     InitializeCriticalSection(§ionA); 
  30.     InitializeCriticalSection(§ionB); 
  31.     Sleep(4000); 
  32.      
  33.     DeleteCriticalSection(§ionB); 
  34.     DeleteCriticalSection(§ionA); 
  35.     return 0; 
  36.      
  37. } 
  38.  
  39. DWORD WINAPI ThreadProc1( 
  40.                          LPVOID lpParameter   // thread data 
  41.                          ) 
  42. { 
  43.      
  44.     while(TRUE) 
  45.     { 
  46.         EnterCriticalSection(§ionA); 
  47.         Sleep(1); 
  48.         EnterCriticalSection(§ionB); 
  49.         if(tickes>0) 
  50.         { 
  51.             cout<<"thread1 sell ticke: "<<tickes--<<endl; 
  52.         } 
  53.         else 
  54.         { 
  55.             break; 
  56.         } 
  57.         LeaveCriticalSection(§ionB); 
  58.         LeaveCriticalSection(§ionA); 
  59.          
  60.     } 
  61.     return 0; 
  62.      
  63. } 
  64.  
  65.  
  66. DWORD WINAPI ThreadProc2( 
  67.                          LPVOID lpParameter   // thread data 
  68.                          ) 
  69. { 
  70.     while(TRUE) 
  71.     { 
  72.         EnterCriticalSection(§ionB); 
  73.         Sleep(1); 
  74.         EnterCriticalSection(§ionA); 
  75.  
  76.         if(tickes>0) 
  77.         { 
  78.             cout<<"thread2 sell ticke: "<<tickes--<<endl; 
  79.         } 
  80.         else 
  81.         { 
  82.             break; 
  83.         } 
  84.         LeaveCriticalSection(§ionA); 
  85.         LeaveCriticalSection(§ionB); 
  86.     } 
  87.     return 0; 
  88. }*/  


 

 

下面是用异步套接字写的聊天程序,跟前面的多线程对比一下:

1新建一个基于单文档的MFC程序

2拉控件成这样:

 

3在CXXAPP文件中的InitInstance中添加WSAStartupXX库添加函数,查看MSDN的WSAStartup

4在CXXDlg中添加成员变量SOCKET m_socket和成员函数 InitSocket;

[cpp]  view plain copy
  1. BOOL CNewChatDlg::InitSocket()  
  2. {  
  3.     m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);  
  4.   
  5.     if(!m_socket)  
  6.     {  
  7.         AfxMessageBox("创建套接字失败");  
  8.         return FALSE;  
  9.     }  
  10.   
  11.     SOCKADDR_IN sockaddr;  
  12.     sockaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);  
  13.     sockaddr.sin_family=AF_INET;  
  14.     sockaddr.sin_port=htons(6000);  
  15.   
  16.     int returnbind;  
  17.     returnbind=bind(m_socket,(SOCKADDR*)&sockaddr,sizeof(SOCKADDR));  
  18.     if(SOCKET_ERROR==returnbind)  
  19.     {  
  20.         AfxMessageBox("绑定失败");  
  21.         return FALSE;  
  22.     }  
  23.   
  24.     int se;  
  25.     se=WSAAsyncSelect(m_socket,m_hWnd,WM_RECV,FD_READ);  
  26.     if(SOCKET_ERROR==se)  
  27.     {  
  28.         AfxMessageBox("创建网络读取时间失败");  
  29.         return FALSE;  
  30.     }  
  31.     return TRUE;  
  32. }  


并在CXXDlg中的OnInitDialog中调用InitSocket();

5自定义消息响应事件

a #define WM_RECV WM_USER+1

b afx_msg void OnRecv(WPARAM wParam,LPARAM lParam);

c ON_MESSAGE(WM_RECV,OnRecv)

[cpp]  view plain copy
  1. void CNewChatDlg::OnRecv(WPARAM wParam,LPARAM lParam)  
  2. {  
  3.     switch(LOWORD(lParam))  
  4.     {  
  5.     case FD_READ:  
  6.         WSABUF wsabuf;  
  7.         wsabuf.buf=new char[200];  
  8.         wsabuf.len=200;  
  9.   
  10.         DWORD dwread;  
  11.         DWORD dwflag=0;  
  12.   
  13.         SOCKADDR_IN sockaddr;  
  14.         int len=sizeof(SOCKADDR);  
  15.   
  16.   
  17.   
  18.         if(SOCKET_ERROR ==WSARecvFrom(m_socket,&wsabuf,1,&dwread,&dwflag,  
  19.             (SOCKADDR*)&sockaddr,&len,NULL,NULL))  
  20.         {  
  21.             AfxMessageBox("接收数据失败");  
  22.             return ;  
  23.         }  
  24.   
  25.       
  26.   
  27.   
  28.         char temp[200];  
  29.         sprintf(temp,"%s 说 %s ",inet_ntoa(sockaddr.sin_addr),wsabuf.buf);  
  30.         CString jieshou;  
  31.         GetDlgItemText(ID_JIESHOU,jieshou);  
  32.         if(jieshou!="")  
  33.         {  
  34.         jieshou+="\r\n";  
  35.         }  
  36.         jieshou+=temp;  
  37.         SetDlgItemText(ID_JIESHOU,jieshou);  
  38.   
  39.         break;  
  40.     }  
  41. }  


6添加发送按钮事件:

[cpp]  view plain copy
  1. void CNewChatDlg::OnSend()   
  2. {  
  3.     // TODO: Add your control notification handler code here  
  4.       
  5.     DWORD dwIp;  
  6.     CString strsend;  
  7.     ((CIPAddressCtrl*)GetDlgItem(ID_IPADDRESS))->GetAddress(dwIp);  
  8.   
  9.     SOCKADDR_IN sockaddr;  
  10.     sockaddr.sin_addr.S_un.S_addr=htonl(dwIp);  
  11.     sockaddr.sin_family=AF_INET;  
  12.     sockaddr.sin_port=htons(6000);  
  13.   
  14.       
  15.   
  16.     GetDlgItemText(ID_FASONG,strsend);  
  17.       
  18.   
  19.       
  20.   
  21.     WSABUF wsabuf;  
  22.     wsabuf.buf=strsend.GetBuffer(strsend.GetLength());  
  23.     wsabuf.len=strsend.GetLength()+1;  
  24.     SetDlgItemText(ID_FASONG,"");  
  25.       
  26.     DWORD dwsent;  
  27.     int len=sizeof(SOCKADDR);  
  28.   
  29.     if(SOCKET_ERROR ==WSASendTo(m_socket,&wsabuf,1,&dwsent,0,  
  30.         (SOCKADDR*)&sockaddr,len,NULL,NULL))  
  31.     {  
  32.         AfxMessageBox("发送数据失败");  
  33.         return ;  
  34.     }  
  35.   
  36.           
  37. }  
  38.   
  39. CNewChatDlg::~CNewChatDlg()  
  40. {  
  41.     closesocket(m_socket);  
  42. }  


 

[cpp]  view plain copy
  1. CNewChatDlg::~CNewChatDlg()  
  2. {  
  3.     closesocket(m_socket);  
  4. }  


 

要熟悉异步套接字和多线程的编程,这个很重要,一定要熟练。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值