VC深入详解之第十六章线程同步与异步套接字编程笔记

实现线程同步的方法:
①互斥对象CreateMutex
②事件对象
③关键代码段
事件对象有两种不同的类型:人工重置的事件对象和自动重置的事件对象。当人工重置的事件独享得到通知时,等待该事件对象的所有线程均变为可调度线程;当一个自动重置的事件对象得到通知时,等待该事件对象的线程中只有一个线程变为可调度线程,同时操作系统会将该事件对象设置为无信号状态,这样,当对所保护的代码执行完成以后,需要调用SetEvent函数将该事件对象设置为有信号状态。而人工重置的事件对象,在一个线程得到该事件对象之后,操作系统并不会将该事件对象设置为无信号状态,除非显示地调用ResetEvent函数将其设置为无信号状态,无责对象会一直是有信号状态。
关键代码段,也成为临界区,工作在用户方式下。它是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。


HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
第二个参数指定该事件是人工重置事件对象,还是自动重置事件对象。如果是人工重置事件对象,当线程等待到该对象的所有权之后,需要调用ResetEvent函数手动地将该事件对象设置为无信号状态。


同样,我们可以通过创建命名的事件对象来保证只有一个实例运行,形如:


g_hEvent = CreateEvent(NULL,FALSE, FALSE, "tickets");


if( g_hEvent)
{
if ( ERROR_ALREADY_EXISTS == GetLastError())
{
cout << "only one instance can run" << endl;
return ;
}
}


关键代码段相关API:
void InitializeCriticalSection( LPCRTICAL_SECTION lpCriticalSection );
EnterCriticalSection
LeaveCriticalSection
DeleteCriticalSection


关键代码段示例:
#include<windows.h>
#include<iostream>
using namespace std;




DWORD WINAPI Fun1Proc( LPVOID lpParameter);
DWORD WINAPI Fun2Proc( LPVOID lpParameter);




int tickets = 20;
CRITICAL_SECTION g_cs;
void main()
{
HANDLE hThread1;
HANDLE hThread2;




/*
if( g_hEvent)
{
if ( ERROR_ALREADY_EXISTS == GetLastError())
{
cout << "only one instance can run" << endl;
return ;
}
}
*/
hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);


InitializeCriticalSection( &g_cs);
Sleep(400000);
DeleteCriticalSection( &g_cs);


}

DWORD WINAPI Fun1Proc( LPVOID lpParameter)
{
while(TRUE)
{
Sleep(1); //双核CPU两个线程是同时运行的,所以会存在访问冲突,所以延迟
EnterCriticalSection( &g_cs);
if( tickets > 0 )
{
cout << "Thread1 sell ticket :" << tickets-- << endl;
LeaveCriticalSection( &g_cs);
}
else
{
LeaveCriticalSection( &g_cs);
break;
}
}
return 0;
}


DWORD WINAPI Fun2Proc( LPVOID lpParameter)
{
while(TRUE)
{
Sleep(1);
EnterCriticalSection( &g_cs);
if( tickets > 0 )
{
cout << "Thread2 sell ticket :" << tickets-- << endl;
LeaveCriticalSection( &g_cs);
}
else
{
LeaveCriticalSection( &g_cs);
break;
}
}
return 0;
}


互斥对象、事件对象与关键代码段的比较:
互斥对象和事件对象都属于内核对象,利用内核对象进行线程同步时,速度较慢,但利用互斥对象和事件对象这样的内核对象,可以在多个进程中的哥哥线程间进行同步。
关键代码段工作在用户方式下,同步速度较快,但在使用关键代码段时,很容易进入死锁状态,因为在等待进入关键代码段时无法设定超时值。


通常,在编写多线程程序并需要实现线程同步时,首先选键代码段,由于它的使用比较简单,如果是在MFC程序中使用的话,可以在类的构造函数调用InitializeCriticalSection函数,在该类的析构函数函数中调用DeleteCriticalSection函数,在所需保护的代码前面调用EnterCriticalSection函数,在访问完所需保护的资源之后,调用LeaveCriticalSection函数。可见,关键代码段在使用上是非常方便的,但有几点需要注意:一是在程序中调用了EnterCriticalSection函数之后,一定要相应地调用LeaveCriticalSection函数,否则其他等待该临界区对象所有权的线程将无法执行。而是如果访问关键代码段时,使用了多个临界区对象,就要注意防止线程死锁的发生。另外,如果需要在多个进程间的各个线程间实现同步的话,可以使用互斥对象和事件对象。


基于消息的异步套接字:
Windows套接字在两种模式下执行I/O操作:阻塞模式和非阻塞模式。在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回。而在非阻塞模式下,Winsock函数无论如何都会立即返回。


int SWAAsyncSelect( SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent)
该函数为制定的套接字请求基于Windows消息的网络事件通知,并自动将该套接字设置为非阻塞模式。


Winsock库中的扩展函数WSASocket将创建套接字。


WSARecvFrom接收数据报类型的数据,并保存数据发送方的地址。
WSASendTo发送数据


实现步骤:
①加载套接字库AfxSocketInit
②创建并初始化套接字
③实现接收端功能
④实现发送端功能


利用主机名实现网络访问:
SOCKADDR_IN这个地址结构中sin_addr.S_un.S_addr需要一个IP地址,所以应该将主机名转换为IP地址。gethostbyname

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值