vc++学习笔记16 线程同步,异步套接字
利用CreateEvent函数,创建线程互斥执行,是线程同步的另一种方式,(锁机制);
#include <windows.h> #include <iostream.h> DWORD WINAPI func1proc(LPVOID lpParameter); DWORD WINAPI func2proc(LPVOID lpParameter); int tickets=100; HANDLE h_hevent;//保存时间对象的句柄, void main() { HANDLE hthread1; HANDLE hthread2; hthread1=CreateThread(NULL,0,func1proc,NULL,0,NULL);//创建一个线程 hthread2=CreateThread(NULL,0,func2proc,NULL,0,NULL); CloseHandle(hthread1); CloseHandle(hthread2); // h_hevent=CreateEvent(NULL,TRUE,FALSE,NULL);//此处创建的是人工重置事件,不能满足要求,因为会输出0状态,人工事件,除非显示调用resentevent,否则一直处于有信号的状态,因此此处建立自动重置事件; h_hevent=CreateEvent(NULL,FALSE,FALSE,NULL);//创建自动重置事件; SetEvent(h_hevent);//讲事件状态设置成有信号状态; Sleep(400); CloseHandle(h_hevent); } DWORD WINAPI func1proc( LPVOID lpParameter // thread data ) { while(true) { WaitForSingleObject(h_hevent,INFINITE);//等待事件有效信号 //ResetEvent() if (tickets>0) { Sleep(1); cout<<"thread1 sells tickets:"<<tickets--<<endl; } else break; //ReleaseMutex(h_hevent);//释放互斥锁,因为他们访问了一个统一的全局变量ticket,释放必须遵循谁拥有,谁释放的原则; //ResetEvent(h_hevent); SetEvent(h_hevent);//重新设置事件状态为有信号 } return 0; } DWORD WINAPI func2proc( LPVOID lpParameter // thread data ) { while(true) { WaitForSingleObject(h_hevent,INFINITE); if (tickets>0) { Sleep(1); cout<<"thread2 sells tickets:"<<tickets--<<endl; } else break; // ReleaseMutex(h_hevent); //ResetEvent(h_hevent); SetEvent(h_hevent);//重设状态有信号事件 } return 0; }//通过创建一个有名事件来实现只有一个实例执行
//通过创建一个命名的事件来实现只有一个实例执行的 h_hevent=CreateEvent(NULL,TRUE,FALSE,"ticket"); if (h_hevent)//判断是否有值 { if (GetLastError()== ERROR_ALREADY_EXISTS)//判断是否已经在执行; { cout<<"only one instance running!"<<endl; return; } }
=====================================================================================================
关键代码段(临界区)同步进程;
VOID InitializeCriticalSection(//初始化代码段 LPCRITICAL_SECTION lpCriticalSection //[out] critical section,使用之前要构造 ); VOID EnterCriticalSection(//进入关键代码段(临界区) LPCRITICAL_SECTION lpCriticalSection // critical section ); VOID LeaveCriticalSection(//离开关键代码段(临界区) LPCRITICAL_SECTION lpCriticalSection // critical section ); VOID DeleteCriticalSection(//删除关键代码段(临界区) LPCRITICAL_SECTION lpCriticalSection // critical section );对共同访问的关键区代码,成对添加;
比较:
互斥对象,事件对象,关键代码段的比较
n 互斥对象和事件对象都属于内核对象,利用内核对象进行线程同步时,较慢,但利用互斥对象和事件对象这俗人内核对象,可以在多个进程中的各个纯种间进行同步
n 关键代码段工作在用户方式下,同步速度快,但很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值
==================================================================================================================
利用基于消息的异步套接字编程建立一个网络聊天程序;
1.建立模板和第一个聊天程序相同,
2,然后添加 家在socket版本信息的内容于initInstance()
BOOL CChat_asyApp::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; }
4,在chatDlg里面添加成员变量m_socket和initsock()函数,初始化socket相关信息
BOOL CChat_asyDlg::InitSocket() { m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);//利用wsasocket来创建套接字的部分 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; } //调用wsaselect函数请求一个基于消息的网络通知; if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,WM_SOCK,FD_READ))//FD_READ注册一个读取的事件 { MessageBox("注册网路读取事件失败"); return FALSE; } return TRUE; }
5 在InitChatDlg(0里面调用initsock()函数;
6添加消息映射函数和定义消息;
ON_MESSAGE(WM_SOCK,OnSock)//消息映射
#d#define WM_SOCK WM_USER+17,添加消息响应函数,并实现之;
afxafx_msg void OnSock(WPARAM,LPARAM);//创建标准消息响应函数
void CChat_asyDlg::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; if(WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,(sockaddr*)&addrFrom,&len,NULL,NULL)==SOCKET_ERROR) { MessageBox("读取失败"); return; } str.Format("%s 说:%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf); CString strTemp; str+="\r\n"; GetDlgItemText(IDC_EDIT_RECV,strTemp); str+=strTemp; SetDlgItemText(IDC_EDIT_RECV,str); break; } }7 添加“send”按钮的消息函数;(将获得的消息输出到对话框上)
void CChat_asyDlg::OnBtnSend() { // TODO: Add your control notification handler code here DWORD dwIP;//接收IP地址; CString strsend; WSABUF wsabuf; DWORD dwSend;//存放实际发送的字节数 int len; ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); /*得到指向CWND的指针,然后强制转换为IP控制*/ SOCKADDR_IN addrTo; addrTo.sin_addr.S_un.S_addr=htonl(INADDR_ANY); 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;//"\0" //发送数据 WSASendTo(m_socket,&wsabuf,1,&dwSend,0,(sockaddr*)&addrTo,sizeof(sockaddr),NULL,NULL); SetDlgItemText(IDC_EDIT_SEND,""); }
(未完全理解)
1
Edit By SolarJupiter