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地址转换为主机名的功能,单线程的聊天室创建完毕!性能并且非常出色!
下面是一些具体的代码:
- #include<windows.h>
- #include<iostream.h>
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter
- );
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter
- );
- int tickes=100;
- HANDLE hEvent;
- int main()
- {
- HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
- HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
- hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);//自动,有信号
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- Sleep(4000);
- return 0;
- }
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- SetEvent(hEvent);
- WaitForSingleObject(hEvent,INFINITE);
- if(tickes>0)
- {
- cout<<"thread1 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- //ReleaseMutex(hEvent);
- ResetEvent(hEvent);
- }
- return 0;
- }
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- SetEvent(hEvent);
- WaitForSingleObject(hEvent,INFINITE);
- if(tickes>0)
- {
- cout<<"thread2 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- //ReleaseMutex(hEvent);
- ResetEvent(hEvent);
- }
- return 0;
- }
- //CreateEvent设置自定的,并且初始有信号
- /*#include<windows.h>
- #include<iostream.h>
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter
- );
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter
- );
- int tickes=100;
- HANDLE hEvent;
- int main()
- {
- HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
- HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
- hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);//自动,有信号
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- Sleep(4000);
- return 0;
- }
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- WaitForSingleObject(hEvent,INFINITE);
- if(tickes>0)
- {
- cout<<"thread1 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- }
- return 0;
- }
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- WaitForSingleObject(hEvent,INFINITE);
- if(tickes>0)
- {
- cout<<"thread2 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- }
- return 0;
- }*/
- //结论是,设置自动的,并且开始有信号(无信号可以调用SetEvent来使无信号到有信号的一个转变)
- //只有一个线程获得了信号,并且运行完后(时间片到了),则下一个线程是无法运行的,因为此时只有一个
- //线程有信号,并且运行完后,马上使得事件对象无信号
- //CreateEvent手动,一开始就无信号
- /*#include<windows.h>
- #include<iostream.h>
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter
- );
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter
- );
- int tickes=100;
- HANDLE hEvent;
- int main()
- {
- HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
- HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
- hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//手动,无信号
- SetEvent(hEvent);
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- Sleep(4000);
- return 0;
- }
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- WaitForSingleObject(hEvent,INFINITE);
- if(tickes>0)
- {
- cout<<"thread1 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- }
- return 0;
- }
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- WaitForSingleObject(hEvent,INFINITE);
- if(tickes>0)
- {
- cout<<"thread2 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- }
- return 0;
- }*/
- //使用手动设置,则任何线程都获得了信号,都可以运行,可以使用ResetEvent使得信号变得无效
- //关键代码段,临界区域
- //如果只是Enter了但是没有Leave则下一个线程获取不了信号,下一个线程得不到执行
- /*
- #include<windows.h>
- #include<iostream.h>
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter
- );
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter
- );
- int tickes=100;
- CRITICAL_SECTION section;
- int main()
- {
- HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
- HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- InitializeCriticalSection(§ion);
- Sleep(4000);
- DeleteCriticalSection(§ion);
- return 0;
- }
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- EnterCriticalSection(§ion);
- if(tickes>0)
- {
- cout<<"thread1 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- LeaveCriticalSection(§ion);
- }
- return 0;
- }
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- EnterCriticalSection(§ion);
- if(tickes>0)
- {
- cout<<"thread2 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- LeaveCriticalSection(§ion);
- }
- return 0;
- }*/
- //死锁的体现
- /*#include<windows.h>
- #include<iostream.h>
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter
- );
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter
- );
- int tickes=100;
- CRITICAL_SECTION sectionA;
- CRITICAL_SECTION sectionB;
- int main()
- {
- HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
- HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- InitializeCriticalSection(§ionA);
- InitializeCriticalSection(§ionB);
- Sleep(4000);
- DeleteCriticalSection(§ionB);
- DeleteCriticalSection(§ionA);
- return 0;
- }
- DWORD WINAPI ThreadProc1(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- EnterCriticalSection(§ionA);
- Sleep(1);
- EnterCriticalSection(§ionB);
- if(tickes>0)
- {
- cout<<"thread1 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- LeaveCriticalSection(§ionB);
- LeaveCriticalSection(§ionA);
- }
- return 0;
- }
- DWORD WINAPI ThreadProc2(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- EnterCriticalSection(§ionB);
- Sleep(1);
- EnterCriticalSection(§ionA);
- if(tickes>0)
- {
- cout<<"thread2 sell ticke: "<<tickes--<<endl;
- }
- else
- {
- break;
- }
- LeaveCriticalSection(§ionA);
- LeaveCriticalSection(§ionB);
- }
- return 0;
- }*/
下面是用异步套接字写的聊天程序,跟前面的多线程对比一下:
1新建一个基于单文档的MFC程序
2拉控件成这样:
3在CXXAPP文件中的InitInstance中添加WSAStartupXX库添加函数,查看MSDN的WSAStartup
4在CXXDlg中添加成员变量SOCKET m_socket和成员函数 InitSocket;
- BOOL CNewChatDlg::InitSocket()
- {
- m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);
- if(!m_socket)
- {
- AfxMessageBox("创建套接字失败");
- return FALSE;
- }
- SOCKADDR_IN sockaddr;
- sockaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
- sockaddr.sin_family=AF_INET;
- sockaddr.sin_port=htons(6000);
- int returnbind;
- returnbind=bind(m_socket,(SOCKADDR*)&sockaddr,sizeof(SOCKADDR));
- if(SOCKET_ERROR==returnbind)
- {
- AfxMessageBox("绑定失败");
- return FALSE;
- }
- int se;
- se=WSAAsyncSelect(m_socket,m_hWnd,WM_RECV,FD_READ);
- if(SOCKET_ERROR==se)
- {
- AfxMessageBox("创建网络读取时间失败");
- return FALSE;
- }
- return TRUE;
- }
并在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)
d
- void CNewChatDlg::OnRecv(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 sockaddr;
- int len=sizeof(SOCKADDR);
- if(SOCKET_ERROR ==WSARecvFrom(m_socket,&wsabuf,1,&dwread,&dwflag,
- (SOCKADDR*)&sockaddr,&len,NULL,NULL))
- {
- AfxMessageBox("接收数据失败");
- return ;
- }
- char temp[200];
- sprintf(temp,"%s 说 %s ",inet_ntoa(sockaddr.sin_addr),wsabuf.buf);
- CString jieshou;
- GetDlgItemText(ID_JIESHOU,jieshou);
- if(jieshou!="")
- {
- jieshou+="\r\n";
- }
- jieshou+=temp;
- SetDlgItemText(ID_JIESHOU,jieshou);
- break;
- }
- }
6添加发送按钮事件:
- void CNewChatDlg::OnSend()
- {
- // TODO: Add your control notification handler code here
- DWORD dwIp;
- CString strsend;
- ((CIPAddressCtrl*)GetDlgItem(ID_IPADDRESS))->GetAddress(dwIp);
- SOCKADDR_IN sockaddr;
- sockaddr.sin_addr.S_un.S_addr=htonl(dwIp);
- sockaddr.sin_family=AF_INET;
- sockaddr.sin_port=htons(6000);
- GetDlgItemText(ID_FASONG,strsend);
- WSABUF wsabuf;
- wsabuf.buf=strsend.GetBuffer(strsend.GetLength());
- wsabuf.len=strsend.GetLength()+1;
- SetDlgItemText(ID_FASONG,"");
- DWORD dwsent;
- int len=sizeof(SOCKADDR);
- if(SOCKET_ERROR ==WSASendTo(m_socket,&wsabuf,1,&dwsent,0,
- (SOCKADDR*)&sockaddr,len,NULL,NULL))
- {
- AfxMessageBox("发送数据失败");
- return ;
- }
- }
- CNewChatDlg::~CNewChatDlg()
- {
- closesocket(m_socket);
- }
- CNewChatDlg::~CNewChatDlg()
- {
- closesocket(m_socket);
- }
要熟悉异步套接字和多线程的编程,这个很重要,一定要熟练。