1、事件对象:也属于内核对象(mutex属于内核对象),包含以下三个成员
1> 使用计数
2> 用于指明该事件是一个自动重置的事件还是一个人工重置的是件的bool值
3> 用于指明该事件处于已通知状态还是未通知状态的bool值
2、实现线程同步:利用事件对象
1> 相关函数:
CreateEvent函数:创建或打开一个命名的或匿名的事件对象
ResetEvent函数:把指定的事件对象设置为无信号状态
SetEvent函数:把指定的事件对象设置为有信号状态
2> 为了实现线程间的同步,不应该使用人工重置的事件对象,而应该使用自动重置的事件对象
3、保证应用程序只有一个实例运行:通过创建一个命名的事件对象
g_hEvent=CreateEvent(NULL, false, true, _T("named"));
if(g_hEvent)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"Only one instance can run!"<<endl;
return 0;
}
}
4、三种线程同步方式:互斥对象、事件对象、关键代码段 (P605)
1> 在编写多线程并且需要实现线程同步时,首选关键代码段
2> 另外需要在多个进程间的各个线程间实现同步的话,可以使用互斥对象和事件对象
5、代码:
// Tread_Event.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <windows.h>
#include <iostream>
using std::cout;
using std::endl;
DWORD WINAPI Fun1Proc(
LPVOID lpParameter
);
DWORD WINAPI Fun2Proc(
LPVOID lpParameter
);
int tickets=100;
HANDLE g_hEvent;
int main()
//g_hEvent=CreateEvent(NULL, true, false, NULL);
//SetEvent(g_hEvent);
//通过命名的事件对象实现只有一个实例运行,必须是命名的事件对象
g_hEvent=CreateEvent(NULL, false, true, _T("named"));
if(g_hEvent)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"Only one instance can run!"<<endl;
return 0;
}
}
HANDLE pThread1=CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
HANDLE pThread2=CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(pThread1);
CloseHandle(pThread2);
Sleep(40000);
return 0;
}
DWORD WINAPI Fun1Proc(
LPVOID lpParameter
)
{
while(true)
{
WaitForSingleObject(g_hEvent, INFINITE);
//ResetEvent(g_hEvent);
if(tickets>0)
{
Sleep(1);
cout<<"Thread1 sell ticket:"<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);
}
return 0;
}
DWORD WINAPI Fun2Proc(
LPVOID lpParameter
)
{
while(true)
{
WaitForSingleObject(g_hEvent, INFINITE);
//ResetEvent(g_hEvent);
if(tickets>0)
{
Sleep(1);
cout<<"Thread2 sell ticket:"<<tickets--<<endl;
}
else
break;
SetEvent(g_hEvent);
}
return 0;
}
6、关键代码段:
#include <windows.h>
#include <iostream>
using std::cout;
using std::endl;
DWORD WINAPI Fun1Proc(
LPVOID lpParameter
);
DWORD WINAPI Fun2Proc(
LPVOID lpParameter
);
int tickets=100;
CRITICAL_SECTION g_cs;
int main()
{
InitializeCriticalSection(&g_cs);
HANDLE pThread1=CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
HANDLE pThread2=CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(pThread1);
CloseHandle(pThread2);
Sleep(4000);
DeleteCriticalSection(&g_cs);
return 0;
}
DWORD WINAPI Fun1Proc(
LPVOID lpParameter
)
{
while(true)
{
EnterCriticalSection(&g_cs);
if(tickets>0)
{
Sleep(1);
cout<<"Thread1 sell ticket:"<<tickets--<<endl;
}
else
break;
//LeaveCriticalSection(&g_cs);
}
return 0;
}
DWORD WINAPI Fun2Proc(
LPVOID lpParameter
)
{
while(true)
{
EnterCriticalSection(&g_cs);
if(tickets>0)
{
Sleep(1);
cout<<"Thread2 sell ticket:"<<tickets--<<endl;
}
else
break;
LeaveCriticalSection(&g_cs);
}
//cout<<"thread2 is running"<<endl;
return 0;
}
7、线程死锁:
#include <windows.h>
#include <iostream>
using std::cout;
using std::endl;
DWORD WINAPI Fun1Proc(
LPVOID lpParameter
);
DWORD WINAPI Fun2Proc(
LPVOID lpParameter
);
int tickets=100;
CRITICAL_SECTION g_csA;
CRITICAL_SECTION g_csB;
int main()
{
InitializeCriticalSection(&g_csA);
InitializeCriticalSection(&g_csB);
HANDLE pThread1=CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
HANDLE pThread2=CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(pThread1);
CloseHandle(pThread2);
Sleep(4000);
DeleteCriticalSection(&g_csA);
DeleteCriticalSection(&g_csB);
return 0;
}
DWORD WINAPI Fun1Proc(
LPVOID lpParameter
)
{
while(true)
{
EnterCriticalSection(&g_csA);
Sleep(1); //睡眠1ms,表示放弃CUP执行机会(时间片),跳转到thread2
EnterCriticalSection(&g_csB);
if(tickets>0)
{
Sleep(1);
cout<<"Thread1 sell ticket:"<<tickets--<<endl;
}
else
break;
LeaveCriticalSection(&g_csB);//释放的顺序不影响
LeaveCriticalSection(&g_csA);
}
return 0;
}
DWORD WINAPI Fun2Proc(
LPVOID lpParameter
)
{
while(true)
{
EnterCriticalSection(&g_csB);
Sleep(1);//睡眠1ms,表示放弃CUP执行机会(时间片),跳转到thread1
EnterCriticalSection(&g_csA);
if(tickets>0)
{
Sleep(1);
cout<<"Thread2 sell ticket:"<<tickets--<<endl;
}
else
break;
LeaveCriticalSection(&g_csA);
LeaveCriticalSection(&g_csB);
}
//cout<<"thread2 is running"<<endl;
return 0;
}
8、采用基于消息的异步套接字实现网络聊天室程序
1>加载套接字库:
#pragma comment(lib, "ws2_32.lib ")
#include <winsock2.h>
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;
}
添加析构函数:
CChatSyncApp::~CChatSyncApp()
{
WSACleanup();
}
2> 创建套接字:
SOCKET m_socket;
m_socket=0; //构造函数中
添加析构函数:
CChatSyncDlg::~CChatSyncDlg()
{
closesocket(m_socket);
}
3> 定义套接字初始化函数:
BOOL CChatSyncDlg::InitSocket()
{
m_socket=WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, 0);
if(INVALID_SOCKET==m_socket)
{
MessageBox(_T("创建套接字失败!"));
return false;
}
SOCKADDR_IN addrSock;
addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSock.sin_family=AF_INET;
addrSock.sin_port=htons(5004);
if( SOCKET_ERROR==bind(m_socket, (SOCKADDR* )&addrSock, sizeof(SOCKADDR)) )
{
MessageBox(_T("绑定失败!"));
return false;
}
//下面的语句为指定套接字请求基于Windows消息的网络事件通知,并自动将该套接字设置为非阻塞模式
if( SOCKET_ERROR==WSAAsyncSelect(m_socket, this->m_hWnd, UM_SOCK, FD_READ) )
{
MessageBox(_T("注册网络读取事件失败!")); //基于Windows消息的网络事件通知
return false;
}
return true;
}
并在主Dlg类的OnInitDialog函数中调用,以初始化套接字
4> 自定义消息及消息相应函数,(自定义消息步骤见chapter09)在消息响应函数中接收消息并显示消息
LRESULT CChatSyncDlg::OnSock(WPARAM wParam, LPARAM lParam)
{
switch(LOWORD(lParam))
{
case FD_READ:
WSABUF wsabuf;
wsabuf.buf=new char[200];
wsabuf.len=200;
SOCKADDR_IN addrFrom;
int len=sizeof(SOCKADDR);
DWORD dwRead;
DWORD dwFlags=0;
if(SOCKET_ERROR ==WSARecvFrom(m_socket, &wsabuf, 1, &dwRead, &dwFlags, (SOCKADDR* )&addrFrom, &len, 0, 0))
{
delete[] wsabuf.buf; //此布易漏
MessageBox(_T("接收数据失败!"));
return 0;
}
CString str;
CString strTemp;
str.Format(_T("%s 说: %s"), inet_ntoa(addrFrom.sin_addr), wsabuf.buf);
GetDlgItemText(IDC_EDIT_SEND, strTemp); //保存已有数据
str+="/t/n";
str+=strTemp;
SetDlgItemText(IDC_EDIT_RECV, str);
delete[] wsabuf.buf; //需要手动删除
}
return 0;
}
5> 实现发送端功能,编写发送按钮的响应函数
void CChatSyncDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString strSend;
GetDlgItemText(IDC_EDIT_SEND, strSend);
WSABUF wsabuf;
int len=strSend.GetLength();
wsabuf.buf=strSend.GetBuffer(strSend.GetLength());
wsabuf.len=strSend.GetLength()+1; //多发送一个BYTE
DWORD dwIP;
((CIPAddressCtrl* )GetDlgItem(IDC_IP))->GetAddress(dwIP);
SOCKADDR_IN addrSend;
addrSend.sin_addr.S_un.S_addr=htonl(dwIP);
addrSend.sin_family=AF_INET;
addrSend.sin_port=htons(5004);
DWORD dwSend;
if(SOCKET_ERROR ==WSASendTo(m_socket, &wsabuf, 1, &dwSend, 0, (SOCKADDR* )&addrSend, sizeof(SOCKADDR), NULL, NULL))
{
MessageBox(_T("发送数据失败!"));
return ;
}
SetDlgItemText(IDC_EDIT_SEND, _T(""));
}
注:本程序采用了异步套接字机制在同一线程中完成了接收端和发送端的功能。
在编写网络应用程序时,采用异步选择机制可以提高网络应用程序的性能,如果再配合多线程技术,将打打提高所编写的网络应用程序的性能。
9、通过IP地址获得主机名:
HOSTENT* pHost;
pHost=gethostbyaddr((char* )&addrFrom.sin_addr.S_un.S_addr, 4, AF_INET);
str.Format(_T("%s 说: %s"), pHost->h_name, wsabuf.buf); //pHost->h_name即为所得主机名
10、通过主机名获得IP地址:
HOSTENT* pHost=gethostbyname(strHostName);
addrSend.sin_addr.S_un.S_addr=*((DWORD* )pHost->h_addr_list[0]); //主机名所对应的IP中的第一个IP地址
11、因为网络状况瞬息万变,所以总是应该对函数的返回值进行判断,如果发生错误就要进行相应处理。
在Windows平台下,为了编写高性能的网络应用程序,出了要对协议本身有所了解以外,还需要了解网络应用程序在Windows平台下工作的原理。
VC++深入详解·chapter16·笔记
最新推荐文章于 2016-10-20 11:31:00 发布