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+1

7,添加消息响应函数,并实现之;

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


原文地址:https://www.cnblogs.com/HuaiNianCiSheng/p/3074713.html